          raise NotImplementedError("pad does not support constant_value != 0")
      return plaidml.op.reflection_padding(data, paddings)
  
  
  def is_plaidml_error(error):
      """ Test whether the given exception is a plaidml Exception.
  
      error: :class:`Exception`
          The generated error
  
 
</s> remove def pad(data, paddings, mode="CONSTANT", name=None, constant_value=0):
    """ PlaidML Pad """
 </s> add def pad(data: plaidml.tile.Value,
        paddings,
        mode: str = "CONSTANT",
        name: Optional[str] = None,  # pylint:disable=unused-argument
        constant_value: int = 0) -> plaidml.tile.Value:
    """ PlaidML Pad

    Notes
    -----
    Currently only Reflect padding is supported.

    Parameters
    ----------
    data :class:`plaidm.tile.Value`
        The tensor to pad
    mode: str, optional
        The padding mode to use. Default: `"CONSTANT"`
    name: str, optional
        The name for the operation. Unused but kept for consistency with tf.pad. Default: ``None``
    constant_value: int, optional
        The value to pad the Tensor with. Default: `0`

    Returns
    -------
    :class:`plaidm.tile.Value`
        The padded tensor
    """ </s> remove class MSSIMLoss(DSSIMObjective):  # pylint:disable=too-few-public-methods
    """ Multiscale Structural Similarity Loss Function

    Parameters
    ----------
    k_1: float, optional
        Parameter of the SSIM. Default: `0.01`
    k_2: float, optional
        Parameter of the SSIM. Default: `0.03`
    filter_size: int, optional
        size of gaussian filter Default: `11`
    filter_sigma: float, optional
        Width of gaussian filter Default: `1.5`
    max_value: float, optional
        Max value of the output. Default: `1.0`
    power_factors: tuple, optional
        Iterable of weights for each of the scales. The number of scales used is the length of the
        list. Index 0 is the unscaled resolution's weight and each increasing scale corresponds to
        the image being downsampled by 2. Defaults to the values obtained in the original paper.
        Default: (0.0448, 0.2856, 0.3001, 0.2363, 0.1333)

    Notes
    ------
    You should add a regularization term like a l2 loss in addition to this one.
    """
    def __init__(self,
                 k_1: float = 0.01,
                 k_2: float = 0.03,
                 filter_size: int = 11,
                 filter_sigma: float = 1.5,
                 max_value: float = 1.0,
                 power_factors: Tuple[float, ...] = (0.0448, 0.2856, 0.3001, 0.2363, 0.1333)
                 ) -> None:
        super().__init__(k_1=k_1,
                         k_2=k_2,
                         filter_size=filter_size,
                         filter_sigma=filter_sigma,
                         max_value=max_value)
        self._power_factors = K.constant(power_factors)

    def _get_smallest_size(self, size: int, idx: int) -> int:
        """ Recursive function to obtain the smallest size that the image will be scaled to.
        for MS-SSIM

        Parameters
        ----------
        size: int
            The current scaled size to iterate through
        idx: int
            The current iteration to be performed. When iteration hits zero the value will
            be returned

        Returns
        -------
        int
            The smallest size the image will be scaled to based on the original image size and
            the amount of scaling factors that will occur
        """
        logger.debug("scale id: %s, size: %s", idx, size)
        if idx > 0:
            size = self._get_smallest_size(size // 2, idx - 1)
        return size

    @classmethod
    def _shrink_images(cls, images: List[plaidml.tile.Value]) -> List[plaidml.tile.Value]:
        """ Reduce the dimensional space of a batch of images in half. If the images are an odd
        number of pixels then pad them to an even dimension prior to shrinking

        All incoming images are assumed square.

        Parameters
        ----------
        images: list
            The y_true, y_pred batch of images to be shrunk

        Returns
        -------
        list
            The y_true, y_pred batch shrunk by half
        """
        if any(x % 2 != 0 for x in K.int_shape(images[1])[1:2]):
            images = [pad(img,
                          [[0, 0], [0, 1], [0, 1], [0, 0]],
                          mode="REFLECT")
                      for img in images]

        images = [K.pool2d(img, (2, 2), strides=(2, 2), padding="valid", pool_mode="avg")
                  for img in images]

        return images

    def _get_ms_ssim(self,
                     y_true: plaidml.tile.Value,
                     y_pred: plaidml.tile.Value) -> plaidml.tile.Value:
        """ Obtain the Multiscale Stuctural Similarity metric.

        Parameters
        ----------
        y_true: :class:`plaidml.tile.Value`
            The input batch of ground truth images
        y_pred: :class:`plaidml.tile.Value`
            The input batch of predicted images

        Returns
        -------
        :class:`plaidml.tile.Value`
            The MS-SSIM for the given images
        """
        im_size = K.int_shape(y_pred)[1]
        # filter size cannot be larger than the smallest scale
        recursions = K.int_shape(self._power_factors)[0]
        smallest_scale = self._get_smallest_size(im_size, recursions - 1)
        if smallest_scale < self._filter_size:
            self._filter_size = smallest_scale
            self._kernel = self._get_kernel()

        images = [y_true, y_pred]
        contrasts = []

        for idx in range(recursions):
            images = self._shrink_images(images) if idx > 0 else images
            ssim, contrast = self._get_ssim(*images)

            if idx < recursions - 1:
                contrasts.append(K.relu(K.expand_dims(contrast, axis=-1)))

        contrasts.append(K.relu(K.expand_dims(ssim, axis=-1)))
        mcs_and_ssim = K.concatenate(contrasts, axis=-1)
        ms_ssim = K.pow(mcs_and_ssim, self._power_factors)

        # K.prod does not work in plaidml so slow recursion it is
        out = ms_ssim[..., 0]
        for idx in range(1, recursions):
            out *= ms_ssim[..., idx]
        return out

    def __call__(self,
                 y_true: plaidml.tile.Value,
                 y_pred: plaidml.tile.Value) -> plaidml.tile.Value:
        """ Call the MS-SSIM Loss Function.

        Parameters
        ----------
        y_true: :class:`plaidml.tile.Value`
            The ground truth value
        y_pred: :class:`plaidml.tile.Value`
            The predicted value

        Returns
        -------
        :class:`plaidml.tile.Value`
            The MS-SSIM Loss value
        """
        ms_ssim = self._get_ms_ssim(y_true, y_pred)
        retval = 1. - ms_ssim
        return K.mean(retval)


 </s> add  </s> remove class GMSDLoss():  # pylint:disable=too-few-public-methods
    """ Gradient Magnitude Similarity Deviation Loss.

    Improved image quality metric over MS-SSIM with easier calculations

    References
    ----------
    http://www4.comp.polyu.edu.hk/~cslzhang/IQA/GMSD/GMSD.htm
    https://arxiv.org/ftp/arxiv/papers/1308/1308.3052.pdf
    """
    def __call__(self,
                 y_true: plaidml.tile.Value,
                 y_pred: plaidml.tile.Value) -> plaidml.tile.Value:
        """ Return the Gradient Magnitude Similarity Deviation Loss.

        Parameters
        ----------
        y_true: :class:`plaidml.tile.Value`
            The ground truth value
        y_pred: :class:`plaidml.tile.Value`
            The predicted value

        Returns
        -------
        :class:`plaidml.tile.Value`
            The loss value
        """
        image_shape = K.int_shape(y_pred)
        true_edge = self._scharr_edges(y_true, True, image_shape)
        pred_edge = self._scharr_edges(y_pred, True, image_shape)
        ephsilon = 0.0025
        upper = 2.0 * true_edge * pred_edge
        lower = K.square(true_edge) + K.square(pred_edge)
        gms = (upper + ephsilon) / (lower + ephsilon)
        gmsd = K.std(gms, axis=(1, 2, 3), keepdims=True)
        gmsd = K.squeeze(gmsd, axis=-1)
        return gmsd

    @classmethod
    def _scharr_edges(cls,
                      image: plaidml.tile.Value,
                      magnitude: bool,
                      image_shape: Tuple[None, int, int, int]) -> plaidml.tile.Value:
        """ Returns a tensor holding modified Scharr edge maps.

        Parameters
        ----------
        image: :class:`plaidml.tile.Value`
            Image tensor with shape [batch_size, h, w, d] and type float32. The image(s) must be
            2x2 or larger.
        magnitude: bool
            Boolean to determine if the edge magnitude or edge direction is returned
        image_shape: tuple
            The shape of the incoming image

        Returns
        -------
        :class:`plaidml.tile.Value`
            Tensor holding edge maps for each channel. Returns a tensor with shape `[batch_size, h,
            w, d, 2]` where the last two dimensions hold `[[dy[0], dx[0]], [dy[1], dx[1]], ...,
            [dy[d-1], dx[d-1]]]` calculated using the Scharr filter.
        """
        # Define vertical and horizontal Scharr filters.
        # 5x5 modified Scharr kernel ( reshape to (5,5,1,2) )
        matrix = np.array([[[[0.00070, 0.00070]],
                            [[0.00520, 0.00370]],
                            [[0.03700, 0.00000]],
                            [[0.00520, -0.0037]],
                            [[0.00070, -0.0007]]],
                           [[[0.00370, 0.00520]],
                            [[0.11870, 0.11870]],
                            [[0.25890, 0.00000]],
                            [[0.11870, -0.1187]],
                            [[0.00370, -0.0052]]],
                           [[[0.00000, 0.03700]],
                            [[0.00000, 0.25890]],
                            [[0.00000, 0.00000]],
                            [[0.00000, -0.2589]],
                            [[0.00000, -0.0370]]],
                           [[[-0.0037, 0.00520]],
                            [[-0.1187, 0.11870]],
                            [[-0.2589, 0.00000]],
                            [[-0.1187, -0.1187]],
                            [[-0.0037, -0.0052]]],
                           [[[-0.0007, 0.00070]],
                            [[-0.0052, 0.00370]],
                            [[-0.0370, 0.00000]],
                            [[-0.0052, -0.0037]],
                            [[-0.0007, -0.0007]]]])
        # num_kernels = [2]
        kernels = K.constant(matrix, dtype='float32')
        kernels = K.tile(kernels, [1, 1, image_shape[-1], 1])

        # Use depth-wise convolution to calculate edge maps per channel.
        # Output tensor has shape [batch_size, h, w, d * num_kernels].
        pad_sizes = [[0, 0], [2, 2], [2, 2], [0, 0]]
        padded = pad(image, pad_sizes, mode='REFLECT')
        output = K.depthwise_conv2d(padded, kernels)

        # TODO magnitude not implemented for plaidml
        if not magnitude:  # direction of edges
            raise FaceswapError("Magnitude for GMSD Loss is not implemented in PlaidML")
        #    # Reshape to [batch_size, h, w, d, num_kernels].
        #    shape = K.concatenate([image_shape, num_kernels], axis=0)
        #    output = K.reshape(output, shape=shape)
        #    output.set_shape(static_image_shape.concatenate(num_kernels))
        #    output = tf.atan(K.squeeze(output[:, :, :, :, 0] / output[:, :, :, :, 1], axis=None))
        # magnitude of edges -- unified x & y edges don't work well with Neural Networks
        return output


 </s> add  </s> remove class DSSIMObjective():  # pylint:disable=too-few-public-methods
    """ DSSIM Loss Functions

    Difference of Structural Similarity (DSSIM loss function).

    Adapted from :func:`tensorflow.image.ssim` for a pure keras implentation.

    Notes
    -----
    Channels last only. Assumes all input images are the same size and square

    Parameters
    ----------
    k_1: float, optional
        Parameter of the SSIM. Default: `0.01`
    k_2: float, optional
        Parameter of the SSIM. Default: `0.03`
    filter_size: int, optional
        size of gaussian filter Default: `11`
    filter_sigma: float, optional
        Width of gaussian filter Default: `1.5`
    max_value: float, optional
        Max value of the output. Default: `1.0`

    Notes
    ------
    You should add a regularization term like a l2 loss in addition to this one.
    """
    def __init__(self,
                 k_1: float = 0.01,
                 k_2: float = 0.03,
                 filter_size: int = 11,
                 filter_sigma: float = 1.5,
                 max_value: float = 1.0) -> None:
        self._filter_size = filter_size
        self._filter_sigma = filter_sigma
        self._kernel = self._get_kernel()

        compensation = 1.0
        self._c1 = (k_1 * max_value) ** 2
        self._c2 = ((k_2 * max_value) ** 2) * compensation

    def _get_kernel(self) -> tf.Tensor:
        """ Obtain the base kernel for performing depthwise convolution.

        Returns
        -------
        :class:`tf.Tensor`
            The gaussian kernel based on selected size and sigma
        """
        coords = np.arange(self._filter_size, dtype="float32")
        coords -= (self._filter_size - 1) / 2.

        kernel = np.square(coords)
        kernel *= -0.5 / np.square(self._filter_sigma)
        kernel = np.reshape(kernel, (1, -1)) + np.reshape(kernel, (-1, 1))
        kernel = K.constant(np.reshape(kernel, (1, -1)))
        kernel = K.softmax(kernel)
        kernel = K.reshape(kernel, (self._filter_size, self._filter_size, 1, 1))
        return kernel

    @classmethod
    def _depthwise_conv2d(cls, image: tf.Tensor, kernel: tf.Tensor) -> tf.Tensor:
        """ Perform a standardized depthwise convolution.

        Parameters
        ----------
        image: :class:`tf.Tensor`
            Batch of images, channels last, to perform depthwise convolution
        kernel: :class:`tf.Tensor`
            convolution kernel

        Returns
        -------
        :class:`tf.Tensor`
            The output from the convolution
        """
        return K.depthwise_conv2d(image, kernel, strides=(1, 1), padding="valid")

    def _get_ssim(self, y_true: tf.Tensor, y_pred: tf.Tensor) -> Tuple[tf.Tensor, tf.Tensor]:
        """ Obtain the structural similarity between a batch of true and predicted images.

        Parameters
        ----------
        y_true: :class:`tf.Tensor`
            The input batch of ground truth images
        y_pred: :class:`tf.Tensor`
            The input batch of predicted images

        Returns
        -------
        :class:`tf.Tensor`
            The SSIM for the given images
        :class:`tf.Tensor`
            The Contrast for the given images
        """
        channels = K.int_shape(y_true)[-1]
        kernel = K.tile(self._kernel, (1, 1, channels, 1))

        # SSIM luminance measure is (2 * mu_x * mu_y + c1) / (mu_x ** 2 + mu_y ** 2 + c1)
        mean_true = self._depthwise_conv2d(y_true, kernel)
        mean_pred = self._depthwise_conv2d(y_pred, kernel)
        num_lum = mean_true * mean_pred * 2.0
        den_lum = K.square(mean_true) + K.square(mean_pred)
        luminance = (num_lum + self._c1) / (den_lum + self._c1)

        # SSIM contrast-structure measure is (2 * cov_{xy} + c2) / (cov_{xx} + cov_{yy} + c2)
        num_con = self._depthwise_conv2d(y_true * y_pred, kernel) * 2.0
        den_con = self._depthwise_conv2d(K.square(y_true) + K.square(y_pred), kernel)

        contrast = (num_con - num_lum + self._c2) / (den_con - den_lum + self._c2)

        # Average over the height x width dimensions
        axes = (-3, -2)
        ssim = K.mean(luminance * contrast, axis=axes)
        contrast = K.mean(contrast, axis=axes)

        return ssim, contrast

    def __call__(self, y_true: tf.Tensor, y_pred: tf.Tensor) -> tf.Tensor:
        """ Call the DSSIM  or MS-DSSIM Loss Function.

        Parameters
        ----------
        y_true: :class:`tf.Tensor`
            The input batch of ground truth images
        y_pred: :class:`tf.Tensor`
            The input batch of predicted images

        Returns
        -------
        :class:`tf.Tensor`
            The DSSIM or MS-DSSIM for the given images
        """
        ssim = self._get_ssim(y_true, y_pred)[0]
        retval = (1. - ssim) / 2.0
        return K.mean(retval)


 </s> add 
def is_plaidml_error(error: Exception) -> bool:
          raise NotImplementedError("pad does not support constant_value != 0")
      return plaidml.op.reflection_padding(data, paddings)
  
  
def is_plaidml_error(error: Exception) -> bool:
      """ Test whether the given exception is a plaidml Exception.
  
      error: :class:`Exception`
          The generated error
  
keep keep keep keep replace keep keep keep keep keep
 Add Flip Loss Function
    - Add Flip for AMD and TF
    - Split Perceptual Loss functions to own modules
    - Fix allowed input shape for models
    - Allow GUI tooltip to display at higher width 
lib/plaidml_utils.py