# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for tf.layers.convolutional."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import tensorflow as tf

import numpy as np
from keras.legacy_tf_layers import convolutional as conv_layers


class ConvTest(tf.test.TestCase):

  def testInvalidDataFormat(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 3), seed=1)
    with self.assertRaisesRegex(ValueError, 'data_format'):
      conv_layers.conv2d(images, 32, 3, data_format='invalid')

  def testInvalidStrides(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 3), seed=1)
    with self.assertRaisesRegex(ValueError, 'strides'):
      conv_layers.conv2d(images, 32, 3, strides=(1, 2, 3))

    with self.assertRaisesRegex(ValueError, 'strides'):
      conv_layers.conv2d(images, 32, 3, strides=None)

  def testInvalidKernelSize(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 3), seed=1)
    with self.assertRaisesRegex(ValueError, 'kernel_size'):
      conv_layers.conv2d(images, 32, (1, 2, 3))

    with self.assertRaisesRegex(ValueError, 'kernel_size'):
      conv_layers.conv2d(images, 32, None)

  def testCreateConv2D(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 4))
    layer = conv_layers.Conv2D(32, [3, 3], activation=tf.nn.relu)
    output = layer.apply(images)
    if not tf.executing_eagerly():
      self.assertEqual(output.op.name, 'conv2d/Relu')
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height - 2, width - 2, 32])
    self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 4, 32])
    self.assertListEqual(layer.bias.get_shape().as_list(), [32])

  def testConv2DFloat16(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 4), dtype='float16')
    output = conv_layers.conv2d(images, 32, [3, 3], activation=tf.nn.relu)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height - 2, width - 2, 32])

  def testCreateConv2DIntegerKernelSize(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 4))
    layer = conv_layers.Conv2D(32, 3)
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height - 2, width - 2, 32])
    self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 4, 32])
    self.assertListEqual(layer.bias.get_shape().as_list(), [32])

  def testCreateConv2DChannelsFirst(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, 4, height, width))
      layer = conv_layers.Conv2D(32, [3, 3], data_format='channels_first')
      output = layer.apply(images)
      self.assertListEqual(output.get_shape().as_list(),
                           [5, 32, height - 2, width - 2])
      self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 4, 32])
      self.assertListEqual(layer.bias.get_shape().as_list(), [32])

  def testUnknownInputChannels(self):
    with tf.Graph().as_default():
      images = tf.compat.v1.placeholder(tf.float32, (5, 7, 9, None))
      layer = conv_layers.Conv2D(32, [3, 3], activation=tf.nn.relu)
      with self.assertRaisesRegex(
          ValueError, 'The channel dimension of the inputs '
          'should be defined. Found `None`.'):
        _ = layer.apply(images)

      images = tf.compat.v1.placeholder(tf.float32, (5, None, 7, 9))
      layer = conv_layers.Conv2D(32, [3, 3], data_format='channels_first')
      with self.assertRaisesRegex(
          ValueError, 'The channel dimension of the inputs '
          'should be defined. Found `None`.'):
        _ = layer.apply(images)

  def testConv2DPaddingSame(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 32), seed=1)
    layer = conv_layers.Conv2D(64, images.get_shape()[1:3], padding='same')
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(), [5, height, width, 64])

  def testCreateConvWithStrides(self):
    height, width = 6, 8
    # Test strides tuple
    images = tf.random.uniform((5, height, width, 3), seed=1)
    layer = conv_layers.Conv2D(32, [3, 3], strides=(2, 2), padding='same')
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height / 2, width / 2, 32])

    # Test strides integer
    layer = conv_layers.Conv2D(32, [3, 3], strides=2, padding='same')
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height / 2, width / 2, 32])

    # Test unequal strides
    layer = conv_layers.Conv2D(32, [3, 3], strides=(2, 1), padding='same')
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height / 2, width, 32])

  def testCreateConv1D(self):
    width = 7
    data = tf.random.uniform((5, width, 4))
    layer = conv_layers.Conv1D(32, 3, activation=tf.nn.relu)
    output = layer.apply(data)
    if not tf.executing_eagerly():
      self.assertEqual(output.op.name, 'conv1d/Relu')
    self.assertListEqual(output.get_shape().as_list(), [5, width - 2, 32])
    self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 4, 32])
    self.assertListEqual(layer.bias.get_shape().as_list(), [32])

  def testConv1DFloat16(self):
    width = 7
    data = tf.random.uniform((5, width, 4), dtype='float16')
    output = conv_layers.conv1d(data, 32, 3, activation=tf.nn.relu)
    self.assertListEqual(output.get_shape().as_list(), [5, width - 2, 32])

  def testCreateConv1DChannelsFirst(self):
    with tf.Graph().as_default():
      width = 7
      data = tf.random.uniform((5, 4, width))
      layer = conv_layers.Conv1D(32, 3, data_format='channels_first')
      output = layer.apply(data)
      self.assertListEqual(output.get_shape().as_list(), [5, 32, width - 2])
      self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 4, 32])
      self.assertListEqual(layer.bias.get_shape().as_list(), [32])

  def testUnknownInputChannelsConv1D(self):
    with tf.Graph().as_default():
      data = tf.compat.v1.placeholder(tf.float32, (5, 4, None))
      layer = conv_layers.Conv1D(32, 3, activation=tf.nn.relu)
      with self.assertRaisesRegex(
          ValueError, 'The channel dimension of the inputs '
          'should be defined. Found `None`.'):
        _ = layer.apply(data)

      data = tf.compat.v1.placeholder(tf.float32, (5, None, 4))
      layer = conv_layers.Conv1D(32, 3, data_format='channels_first')
      with self.assertRaisesRegex(
          ValueError, 'The channel dimension of the inputs '
          'should be defined. Found `None`.'):
        _ = layer.apply(data)

  def testCreateConv3D(self):
    depth, height, width = 6, 7, 9
    volumes = tf.random.uniform((5, depth, height, width, 4))
    layer = conv_layers.Conv3D(32, [3, 3, 3], activation=tf.nn.relu)
    output = layer.apply(volumes)
    if not tf.executing_eagerly():
      self.assertEqual(output.op.name, 'conv3d/Relu')
    self.assertListEqual(output.get_shape().as_list(),
                         [5, depth - 2, height - 2, width - 2, 32])
    self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 3, 4, 32])
    self.assertListEqual(layer.bias.get_shape().as_list(), [32])

  def testUnknownInputChannelsConv3D(self):
    with tf.Graph().as_default():
      volumes = tf.compat.v1.placeholder(tf.float32, (5, 6, 7, 9, None))
      layer = conv_layers.Conv3D(32, [3, 3, 3], activation=tf.nn.relu)
      with self.assertRaisesRegex(
          ValueError, 'The channel dimension of the inputs '
          'should be defined. Found `None`.'):
        _ = layer.apply(volumes)

  def testConv2DKernelRegularizer(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 4))
      reg = lambda x: 0.1 * tf.reduce_sum(x)
      layer = conv_layers.Conv2D(32, [3, 3], kernel_regularizer=reg)
      layer.apply(images)
      loss_keys = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
      self.assertEqual(len(loss_keys), 1)
      self.evaluate([v.initializer for v in layer.variables])
      self.assertListEqual(
          self.evaluate(layer.losses), self.evaluate(loss_keys))

  def testConv2DBiasRegularizer(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 4))
      reg = lambda x: 0.1 * tf.reduce_sum(x)
      layer = conv_layers.Conv2D(32, [3, 3], bias_regularizer=reg)
      layer.apply(images)
      loss_keys = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
      self.assertEqual(len(loss_keys), 1)
      self.evaluate([v.initializer for v in layer.variables])
      self.assertListEqual(
          self.evaluate(layer.losses), self.evaluate(loss_keys))

  def testConv2DNoBias(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 4))
    layer = conv_layers.Conv2D(
        32, [3, 3], activation=tf.nn.relu, use_bias=False)
    output = layer.apply(images)
    if not tf.executing_eagerly():
      self.assertEqual(output.op.name, 'conv2d/Relu')
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height - 2, width - 2, 32])
    self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 4, 32])
    self.assertEqual(layer.bias, None)

  def testDilatedConv2D(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 4))
    layer = conv_layers.Conv2D(32, [3, 3], dilation_rate=3)
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(), [5, 1, 3, 32])
    self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 4, 32])
    self.assertListEqual(layer.bias.get_shape().as_list(), [32])

    # Test tuple dilation rate
    layer = conv_layers.Conv2D(32, [3, 3], dilation_rate=(1, 3))
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(), [5, height - 2, 3, 32])

  def testFunctionalConv2DReuse(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 3), seed=1)
      conv_layers.conv2d(images, 32, [3, 3], name='conv1')
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)
      conv_layers.conv2d(images, 32, [3, 3], name='conv1', reuse=True)
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)

  def testFunctionalConv2DReuseFromScope(self):
    with tf.Graph().as_default():
      with tf.compat.v1.variable_scope('scope'):
        height, width = 7, 9
        images = tf.random.uniform((5, height, width, 3), seed=1)
        conv_layers.conv2d(images, 32, [3, 3], name='conv1')
        self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)
      with tf.compat.v1.variable_scope('scope', reuse=True):
        conv_layers.conv2d(images, 32, [3, 3], name='conv1')
        self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)

  def testFunctionalConv2DInitializerFromScope(self):
    with tf.Graph().as_default(), self.cached_session():
      with tf.compat.v1.variable_scope(
          'scope', initializer=tf.compat.v1.ones_initializer()):
        height, width = 7, 9
        images = tf.random.uniform((5, height, width, 3), seed=1)
        conv_layers.conv2d(images, 32, [3, 3], name='conv1')
        weights = tf.compat.v1.trainable_variables()
        # Check the names of weights in order.
        self.assertTrue('kernel' in weights[0].name)
        self.assertTrue('bias' in weights[1].name)
        self.evaluate(tf.compat.v1.global_variables_initializer())
        weights = self.evaluate(weights)
        # Check that the kernel weights got initialized to ones (from scope)
        self.assertAllClose(weights[0], np.ones((3, 3, 3, 32)))
        # Check that the bias still got initialized to zeros.
        self.assertAllClose(weights[1], np.zeros((32)))

  def testFunctionalConv2DNoReuse(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 3), seed=1)
      conv_layers.conv2d(images, 32, [3, 3])
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)
      conv_layers.conv2d(images, 32, [3, 3])
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 4)

  def testConstraints(self):
    # Conv1D
    k_constraint = lambda x: x / tf.reduce_sum(x)
    b_constraint = lambda x: x / tf.reduce_max(x)
    conv1d = conv_layers.Conv1D(2, 3,
                                kernel_constraint=k_constraint,
                                bias_constraint=b_constraint)
    inputs = tf.random.uniform((5, 3, 5), seed=1)
    conv1d(inputs)
    self.assertEqual(conv1d.kernel_constraint, k_constraint)
    self.assertEqual(conv1d.bias_constraint, b_constraint)

    # Conv2D
    k_constraint = lambda x: x / tf.reduce_sum(x)
    b_constraint = lambda x: x / tf.reduce_max(x)
    conv2d = conv_layers.Conv2D(2, 3,
                                kernel_constraint=k_constraint,
                                bias_constraint=b_constraint)
    inputs = tf.random.uniform((5, 3, 3, 5), seed=1)
    conv2d(inputs)
    self.assertEqual(conv2d.kernel_constraint, k_constraint)
    self.assertEqual(conv2d.bias_constraint, b_constraint)

    # Conv3D
    k_constraint = lambda x: x / tf.reduce_sum(x)
    b_constraint = lambda x: x / tf.reduce_max(x)
    conv3d = conv_layers.Conv3D(2, 3,
                                kernel_constraint=k_constraint,
                                bias_constraint=b_constraint)
    inputs = tf.random.uniform((5, 3, 3, 3, 5), seed=1)
    conv3d(inputs)
    self.assertEqual(conv3d.kernel_constraint, k_constraint)
    self.assertEqual(conv3d.bias_constraint, b_constraint)

  def testConv3DChannelsFirst(self):
    # Test case for GitHub issue 15655
    with tf.Graph().as_default():
      images = tf.compat.v1.placeholder(
          dtype=tf.float32, shape=[None, 1, 32, 32, 32])
      conv_layers.conv3d(images, 32, 9, data_format='channels_first')


class SeparableConv1DTest(tf.test.TestCase):

  def testInvalidDataFormat(self):
    length = 9
    data = tf.random.uniform((5, length, 3), seed=1)
    with self.assertRaisesRegex(ValueError, 'data_format'):
      conv_layers.separable_conv1d(data, 32, 3, data_format='invalid')

  def testInvalidStrides(self):
    length = 9
    data = tf.random.uniform((5, length, 3), seed=1)
    with self.assertRaisesRegex(ValueError, 'strides'):
      conv_layers.separable_conv1d(data, 32, 3, strides=(1, 2))

    with self.assertRaisesRegex(ValueError, 'strides'):
      conv_layers.separable_conv1d(data, 32, 3, strides=None)

  def testInvalidKernelSize(self):
    length = 9
    data = tf.random.uniform((5, length, 3), seed=1)
    with self.assertRaisesRegex(ValueError, 'kernel_size'):
      conv_layers.separable_conv1d(data, 32, (1, 2))

    with self.assertRaisesRegex(ValueError, 'kernel_size'):
      conv_layers.separable_conv1d(data, 32, None)

  def testCreateSeparableConv1D(self):
    length = 9
    data = tf.random.uniform((5, length, 4))
    layer = conv_layers.SeparableConv1D(32, 3, activation=tf.nn.relu)
    output = layer.apply(data)
    if not tf.executing_eagerly():
      self.assertEqual(output.op.name, 'separable_conv1d/Relu')
    self.assertEqual(output.get_shape().as_list(), [5, length - 2, 32])
    self.assertEqual(layer.depthwise_kernel.get_shape().as_list(), [3, 4, 1])
    self.assertEqual(layer.pointwise_kernel.get_shape().as_list(), [1, 4, 32])
    self.assertEqual(layer.bias.get_shape().as_list(), [32])

  def testCreateSeparableConv1DDepthMultiplier(self):
    length = 9
    data = tf.random.uniform((5, length, 4))
    layer = conv_layers.SeparableConv1D(32, 3, depth_multiplier=2)
    output = layer.apply(data)
    self.assertEqual(output.get_shape().as_list(), [5, length - 2, 32])
    self.assertEqual(layer.depthwise_kernel.get_shape().as_list(), [3, 4, 2])
    self.assertEqual(layer.pointwise_kernel.get_shape().as_list(), [1, 8, 32])
    self.assertEqual(layer.bias.get_shape().as_list(), [32])

  def testCreateSeparableConv1DChannelsFirst(self):
    with tf.Graph().as_default():
      length = 9
      data = tf.random.uniform((5, 4, length))
      layer = conv_layers.SeparableConv1D(32, 3, data_format='channels_first')
      output = layer.apply(data)
      self.assertEqual(output.get_shape().as_list(), [5, 32, length - 2])
      self.assertEqual(layer.depthwise_kernel.get_shape().as_list(), [3, 4, 1])
      self.assertEqual(layer.pointwise_kernel.get_shape().as_list(), [1, 4, 32])
      self.assertEqual(layer.bias.get_shape().as_list(), [32])

  def testSeparableConv1DPaddingSame(self):
    length = 9
    data = tf.random.uniform((5, length, 32), seed=1)
    layer = conv_layers.SeparableConv1D(
        64, length, padding='same')
    output = layer.apply(data)
    self.assertEqual(output.get_shape().as_list(), [5, length, 64])

  def testCreateSeparableConv1DWithStrides(self):
    length = 10
    data = tf.random.uniform((5, length, 3), seed=1)
    layer = conv_layers.SeparableConv1D(32, 3, strides=2, padding='same')
    output = layer.apply(data)
    self.assertEqual(output.get_shape().as_list(), [5, length // 2, 32])

  def testCreateSeparableConv1DWithStridesChannelsFirst(self):
    with tf.Graph().as_default():
      data_format = 'channels_first'
      length = 10
      data = tf.random.uniform((5, 3, length), seed=1)
      layer = conv_layers.SeparableConv1D(
          32, 3, strides=2, padding='same', data_format=data_format)
      output = layer.apply(data)
      self.assertEqual(output.get_shape().as_list(), [5, 32, length // 2])

  def testFunctionalConv1DReuse(self):
    with tf.Graph().as_default():
      length = 10
      data = tf.random.uniform((5, length, 3), seed=1)
      conv_layers.separable_conv1d(data, 32, 3, name='sepconv1')
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 3)
      conv_layers.separable_conv1d(data, 32, 3, name='sepconv1', reuse=True)
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 3)

  def testFunctionalConv1DReuseFromScope(self):
    with tf.Graph().as_default():
      with tf.compat.v1.variable_scope('scope'):
        length = 10
        data = tf.random.uniform((5, length, 3), seed=1)
        conv_layers.separable_conv1d(data, 32, 3, name='sepconv1')
        self.assertEqual(len(tf.compat.v1.trainable_variables()), 3)
      with tf.compat.v1.variable_scope('scope', reuse=True):
        conv_layers.separable_conv1d(data, 32, 3, name='sepconv1')
        self.assertEqual(len(tf.compat.v1.trainable_variables()), 3)

  def testFunctionalConv1DNoReuse(self):
    with tf.Graph().as_default():
      length = 10
      data = tf.random.uniform((5, length, 3), seed=1)
      conv_layers.separable_conv1d(data, 32, 3)
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 3)
      conv_layers.separable_conv1d(data, 32, 3)
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 6)

  def testSeparableConv1DDepthwiseRegularizer(self):
    with tf.Graph().as_default():
      length = 9
      data = tf.random.uniform((5, length, 4))
      reg = lambda x: 0.1 * tf.reduce_sum(x)
      layer = conv_layers.SeparableConv1D(32, 3, depthwise_regularizer=reg)
      layer.apply(data)
      loss_keys = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
      self.assertEqual(len(loss_keys), 1)
      self.evaluate([v.initializer for v in layer.variables])
      self.assertListEqual(
          self.evaluate(layer.losses), self.evaluate(loss_keys))

  def testSeparableConv1DPointwiseRegularizer(self):
    with tf.Graph().as_default():
      length = 9
      data = tf.random.uniform((5, length, 4))
      reg = lambda x: 0.1 * tf.reduce_sum(x)
      layer = conv_layers.SeparableConv1D(32, 3, pointwise_regularizer=reg)
      layer.apply(data)
      loss_keys = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
      self.assertEqual(len(loss_keys), 1)
      self.evaluate([v.initializer for v in layer.variables])
      self.assertListEqual(
          self.evaluate(layer.losses), self.evaluate(loss_keys))

  def testSeparableConv1DBiasRegularizer(self):
    with tf.Graph().as_default():
      length = 9
      data = tf.random.uniform((5, length, 4))
      reg = lambda x: 0.1 * tf.reduce_sum(x)
      layer = conv_layers.SeparableConv1D(32, 3, bias_regularizer=reg)
      layer.apply(data)
      loss_keys = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
      self.assertEqual(len(loss_keys), 1)
      self.evaluate([v.initializer for v in layer.variables])
      self.assertListEqual(
          self.evaluate(layer.losses), self.evaluate(loss_keys))

  def testSeparableConv1DNoBias(self):
    with tf.Graph().as_default():
      length = 9
      data = tf.random.uniform((5, length, 4))
      layer = conv_layers.SeparableConv1D(
          32, 3, activation=tf.nn.relu, use_bias=False)
      output = layer.apply(data)
      self.assertEqual(output.op.name, 'separable_conv1d/Relu')
      self.assertEqual(layer.bias, None)

  def testConstraints(self):
    d_constraint = lambda x: x / tf.reduce_sum(x)
    p_constraint = lambda x: x / tf.reduce_sum(x)
    b_constraint = lambda x: x / tf.reduce_max(x)
    layer = conv_layers.SeparableConv1D(2, 3,
                                        depthwise_constraint=d_constraint,
                                        pointwise_constraint=p_constraint,
                                        bias_constraint=b_constraint)
    inputs = tf.random.uniform((5, 3, 5), seed=1)
    layer(inputs)
    self.assertEqual(layer.depthwise_constraint, d_constraint)
    self.assertEqual(layer.pointwise_constraint, p_constraint)
    self.assertEqual(layer.bias_constraint, b_constraint)


class SeparableConv2DTest(tf.test.TestCase):

  def testInvalidDataFormat(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 3), seed=1)
    with self.assertRaisesRegex(ValueError, 'data_format'):
      conv_layers.separable_conv2d(images, 32, 3, data_format='invalid')

  def testInvalidStrides(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 3), seed=1)
    with self.assertRaisesRegex(ValueError, 'strides'):
      conv_layers.separable_conv2d(images, 32, 3, strides=(1, 2, 3))

    with self.assertRaisesRegex(ValueError, 'strides'):
      conv_layers.separable_conv2d(images, 32, 3, strides=None)

  def testInvalidKernelSize(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 3), seed=1)
    with self.assertRaisesRegex(ValueError, 'kernel_size'):
      conv_layers.separable_conv2d(images, 32, (1, 2, 3))

    with self.assertRaisesRegex(ValueError, 'kernel_size'):
      conv_layers.separable_conv2d(images, 32, None)

  def testCreateSeparableConv2D(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 4))
    layer = conv_layers.SeparableConv2D(32, [3, 3], activation=tf.nn.relu)
    output = layer.apply(images)
    if not tf.executing_eagerly():
      self.assertEqual(output.op.name, 'separable_conv2d/Relu')
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height - 2, width - 2, 32])
    self.assertListEqual(layer.depthwise_kernel.get_shape().as_list(),
                         [3, 3, 4, 1])
    self.assertListEqual(layer.pointwise_kernel.get_shape().as_list(),
                         [1, 1, 4, 32])
    self.assertListEqual(layer.bias.get_shape().as_list(), [32])

  def testCreateSeparableConv2DDepthMultiplier(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 4))
    layer = conv_layers.SeparableConv2D(32, [3, 3], depth_multiplier=2)
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height - 2, width - 2, 32])
    self.assertListEqual(layer.depthwise_kernel.get_shape().as_list(),
                         [3, 3, 4, 2])
    self.assertListEqual(layer.pointwise_kernel.get_shape().as_list(),
                         [1, 1, 8, 32])
    self.assertListEqual(layer.bias.get_shape().as_list(), [32])

  def testCreateSeparableConv2DIntegerKernelSize(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 4))
    layer = conv_layers.SeparableConv2D(32, 3)
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height - 2, width - 2, 32])
    self.assertListEqual(layer.depthwise_kernel.get_shape().as_list(),
                         [3, 3, 4, 1])
    self.assertListEqual(layer.pointwise_kernel.get_shape().as_list(),
                         [1, 1, 4, 32])
    self.assertListEqual(layer.bias.get_shape().as_list(), [32])

  def testCreateSeparableConv2DChannelsFirst(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, 4, height, width))
      layer = conv_layers.SeparableConv2D(
          32, [3, 3], data_format='channels_first')
      output = layer.apply(images)
      self.assertListEqual(output.get_shape().as_list(),
                           [5, 32, height - 2, width - 2])
      self.assertListEqual(layer.depthwise_kernel.get_shape().as_list(),
                           [3, 3, 4, 1])
      self.assertListEqual(layer.pointwise_kernel.get_shape().as_list(),
                           [1, 1, 4, 32])
      self.assertListEqual(layer.bias.get_shape().as_list(), [32])

  def testSeparableConv2DPaddingSame(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 32), seed=1)
    layer = conv_layers.SeparableConv2D(
        64, images.get_shape()[1:3], padding='same')
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(), [5, height, width, 64])

  def testCreateSeparableConvWithStrides(self):
    with tf.Graph().as_default():
      height, width = 6, 8
      # Test strides tuple
      images = tf.random.uniform((5, height, width, 3), seed=1)
      layer = conv_layers.SeparableConv2D(
          32, [3, 3], strides=(2, 2), padding='same')
      output = layer.apply(images)
      self.assertListEqual(output.get_shape().as_list(),
                           [5, height / 2, width / 2, 32])

      # Test strides integer
      layer = conv_layers.SeparableConv2D(32, [3, 3], strides=2, padding='same')
      output = layer.apply(images)
      self.assertListEqual(output.get_shape().as_list(),
                           [5, height / 2, width / 2, 32])

      # Test unequal strides
      layer = conv_layers.SeparableConv2D(
          32, [3, 3], strides=(2, 1), padding='same')
      output = layer.apply(images)
      self.assertListEqual(output.get_shape().as_list(),
                           [5, height / 2, width, 32])

  def testCreateSeparableConvWithStridesChannelsFirst(self):
    with tf.Graph().as_default():
      data_format = 'channels_first'
      height, width = 6, 8
      # Test strides tuple
      images = tf.random.uniform((5, 3, height, width), seed=1)
      layer = conv_layers.SeparableConv2D(
          32, [3, 3], strides=(2, 2), padding='same', data_format=data_format)
      output = layer.apply(images)
      self.assertListEqual(output.get_shape().as_list(),
                           [5, 32, height / 2, width / 2])

      # Test strides integer
      layer = conv_layers.SeparableConv2D(32, [3, 3], strides=2, padding='same',
                                          data_format=data_format)
      output = layer.apply(images)
      self.assertListEqual(output.get_shape().as_list(),
                           [5, 32, height / 2, width / 2])

      # Test unequal strides
      layer = conv_layers.SeparableConv2D(
          32, [3, 3], strides=(2, 1), padding='same', data_format=data_format)
      output = layer.apply(images)
      self.assertListEqual(output.get_shape().as_list(),
                           [5, 32, height / 2, width])

  def testFunctionalConv2DReuse(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 3), seed=1)
      conv_layers.separable_conv2d(images, 32, [3, 3], name='sepconv1')
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 3)
      conv_layers.separable_conv2d(
          images, 32, [3, 3], name='sepconv1', reuse=True)
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 3)

  def testFunctionalConv2DReuseFromScope(self):
    with tf.Graph().as_default():
      with tf.compat.v1.variable_scope('scope'):
        height, width = 7, 9
        images = tf.random.uniform((5, height, width, 3), seed=1)
        conv_layers.separable_conv2d(images, 32, [3, 3], name='sepconv1')
        self.assertEqual(len(tf.compat.v1.trainable_variables()), 3)
      with tf.compat.v1.variable_scope('scope', reuse=True):
        conv_layers.separable_conv2d(images, 32, [3, 3], name='sepconv1')
        self.assertEqual(len(tf.compat.v1.trainable_variables()), 3)

  def testFunctionalConv2DInitializerFromScope(self):
    with tf.Graph().as_default(), self.cached_session():
      with tf.compat.v1.variable_scope(
          'scope', initializer=tf.compat.v1.ones_initializer()):
        height, width = 7, 9
        images = tf.random.uniform((5, height, width, 3), seed=1)
        conv_layers.separable_conv2d(images, 32, [3, 3], name='sepconv1')
        weights = tf.compat.v1.trainable_variables()
        # Check the names of weights in order.
        self.assertTrue('depthwise_kernel' in weights[0].name)
        self.assertTrue('pointwise_kernel' in weights[1].name)
        self.assertTrue('bias' in weights[2].name)
        self.evaluate(tf.compat.v1.global_variables_initializer())
        weights = self.evaluate(weights)
        # Check that the kernel weights got initialized to ones (from scope)
        self.assertAllClose(weights[0], np.ones((3, 3, 3, 1)))
        self.assertAllClose(weights[1], np.ones((1, 1, 3, 32)))
        # Check that the bias still got initialized to zeros.
        self.assertAllClose(weights[2], np.zeros((32)))

  def testFunctionalConv2DNoReuse(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 3), seed=1)
      conv_layers.separable_conv2d(images, 32, [3, 3])
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 3)
      conv_layers.separable_conv2d(images, 32, [3, 3])
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 6)

  def testSeparableConv2DDepthwiseRegularizer(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 4))
      reg = lambda x: 0.1 * tf.reduce_sum(x)
      layer = conv_layers.SeparableConv2D(32, [3, 3], depthwise_regularizer=reg)
      layer.apply(images)
      loss_keys = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
      self.assertEqual(len(loss_keys), 1)
      self.evaluate([v.initializer for v in layer.variables])
      self.assertListEqual(
          self.evaluate(layer.losses), self.evaluate(loss_keys))

  def testSeparableConv2DPointwiseRegularizer(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 4))
      reg = lambda x: 0.1 * tf.reduce_sum(x)
      layer = conv_layers.SeparableConv2D(32, [3, 3], pointwise_regularizer=reg)
      layer.apply(images)
      loss_keys = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
      self.assertEqual(len(loss_keys), 1)
      self.evaluate([v.initializer for v in layer.variables])
      self.assertListEqual(
          self.evaluate(layer.losses), self.evaluate(loss_keys))

  def testSeparableConv2DBiasRegularizer(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 4))
      reg = lambda x: 0.1 * tf.reduce_sum(x)
      layer = conv_layers.SeparableConv2D(32, [3, 3], bias_regularizer=reg)
      layer.apply(images)
      loss_keys = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
      self.assertEqual(len(loss_keys), 1)
      self.evaluate([v.initializer for v in layer.variables])
      self.assertListEqual(
          self.evaluate(layer.losses), self.evaluate(loss_keys))

  def testSeparableConv2DNoBias(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 4))
      layer = conv_layers.SeparableConv2D(
          32, [3, 3], activation=tf.nn.relu, use_bias=False)
      output = layer.apply(images)
      self.assertEqual(output.op.name, 'separable_conv2d/Relu')
      self.assertListEqual(output.get_shape().as_list(),
                           [5, height - 2, width - 2, 32])
      self.assertListEqual(layer.depthwise_kernel.get_shape().as_list(),
                           [3, 3, 4, 1])
      self.assertListEqual(layer.pointwise_kernel.get_shape().as_list(),
                           [1, 1, 4, 32])
      self.assertEqual(layer.bias, None)

  def testConstraints(self):
    d_constraint = lambda x: x / tf.reduce_sum(x)
    p_constraint = lambda x: x / tf.reduce_sum(x)
    b_constraint = lambda x: x / tf.reduce_max(x)
    layer = conv_layers.SeparableConv2D(2, 3,
                                        depthwise_constraint=d_constraint,
                                        pointwise_constraint=p_constraint,
                                        bias_constraint=b_constraint)
    inputs = tf.random.uniform((5, 3, 3, 5), seed=1)
    layer(inputs)
    self.assertEqual(layer.depthwise_constraint, d_constraint)
    self.assertEqual(layer.pointwise_constraint, p_constraint)
    self.assertEqual(layer.bias_constraint, b_constraint)


class Conv2DTransposeTest(tf.test.TestCase):

  def testInvalidDataFormat(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 3), seed=1)
    with self.assertRaisesRegex(ValueError, 'data_format'):
      conv_layers.conv2d_transpose(images, 32, 3, data_format='invalid')

  def testInvalidStrides(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 3), seed=1)
    with self.assertRaisesRegex(ValueError, 'strides'):
      conv_layers.conv2d_transpose(images, 32, 3, strides=(1, 2, 3))

    with self.assertRaisesRegex(ValueError, 'strides'):
      conv_layers.conv2d_transpose(images, 32, 3, strides=None)

  def testInvalidKernelSize(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 3), seed=1)
    with self.assertRaisesRegex(ValueError, 'kernel_size'):
      conv_layers.conv2d_transpose(images, 32, (1, 2, 3))

    with self.assertRaisesRegex(ValueError, 'kernel_size'):
      conv_layers.conv2d_transpose(images, 32, None)

  def testCreateConv2DTranspose(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 4))
    layer = conv_layers.Conv2DTranspose(32, [3, 3], activation=tf.nn.relu)
    output = layer.apply(images)
    if not tf.executing_eagerly():
      self.assertEqual(output.op.name, 'conv2d_transpose/Relu')
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height + 2, width + 2, 32])
    self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 32, 4])
    self.assertListEqual(layer.bias.get_shape().as_list(), [32])

  def testConv2DTransposeFloat16(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 4), dtype='float16')
    output = conv_layers.conv2d_transpose(images, 32, [3, 3],
                                          activation=tf.nn.relu)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height + 2, width + 2, 32])

  def testCreateConv2DTransposeIntegerKernelSize(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 4))
    layer = conv_layers.Conv2DTranspose(32, 3)
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height + 2, width + 2, 32])
    self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 32, 4])
    self.assertListEqual(layer.bias.get_shape().as_list(), [32])

  def testCreateConv2DTransposeChannelsFirst(self):
    height, width = 7, 9
    images = tf.random.uniform((5, 4, height, width))
    layer = conv_layers.Conv2DTranspose(
        32, [3, 3], data_format='channels_first')
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, 32, height + 2, width + 2])
    self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 32, 4])
    self.assertListEqual(layer.bias.get_shape().as_list(), [32])

  def testConv2DTransposePaddingSame(self):
    height, width = 7, 9
    images = tf.random.uniform((5, height, width, 32), seed=1)
    layer = conv_layers.Conv2DTranspose(
        64, images.get_shape()[1:3], padding='same')
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(), [5, height, width, 64])

  def testCreateConv2DTransposeWithStrides(self):
    height, width = 6, 8
    # Test strides tuple
    images = tf.random.uniform((5, height, width, 3), seed=1)
    layer = conv_layers.Conv2DTranspose(
        32, [3, 3], strides=(2, 2), padding='same')
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height * 2, width * 2, 32])

    # Test strides integer
    layer = conv_layers.Conv2DTranspose(32, [3, 3], strides=2, padding='same')
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height * 2, width * 2, 32])

    # Test unequal strides
    layer = conv_layers.Conv2DTranspose(
        32, [3, 3], strides=(2, 1), padding='same')
    output = layer.apply(images)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, height * 2, width, 32])

  def testConv2DTransposeKernelRegularizer(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 4))
      reg = lambda x: 0.1 * tf.reduce_sum(x)
      layer = conv_layers.Conv2DTranspose(32, [3, 3], kernel_regularizer=reg)
      layer.apply(images)
      loss_keys = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
      self.assertEqual(len(loss_keys), 1)
      self.evaluate([v.initializer for v in layer.variables])
      self.assertListEqual(
          self.evaluate(layer.losses), self.evaluate(loss_keys))

  def testConv2DTransposeBiasRegularizer(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 4))
      reg = lambda x: 0.1 * tf.reduce_sum(x)
      layer = conv_layers.Conv2DTranspose(32, [3, 3], bias_regularizer=reg)
      layer.apply(images)
      loss_keys = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
      self.assertEqual(len(loss_keys), 1)
      self.evaluate([v.initializer for v in layer.variables])
      self.assertListEqual(
          self.evaluate(layer.losses), self.evaluate(loss_keys))

  def testConv2DTransposeNoBias(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 4))
      layer = conv_layers.Conv2DTranspose(
          32, [3, 3], activation=tf.nn.relu, use_bias=False)
      output = layer.apply(images)
      self.assertEqual(output.op.name, 'conv2d_transpose/Relu')
      self.assertListEqual(output.get_shape().as_list(),
                           [5, height + 2, width + 2, 32])
      self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 32, 4])
      self.assertEqual(layer.bias, None)

  def testFunctionalConv2DTransposeReuse(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 3), seed=1)
      conv_layers.conv2d_transpose(images, 32, [3, 3], name='deconv1')
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)
      conv_layers.conv2d_transpose(
          images, 32, [3, 3], name='deconv1', reuse=True)
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)

  def testFunctionalConv2DTransposeReuseFromScope(self):
    with tf.Graph().as_default():
      with tf.compat.v1.variable_scope('scope'):
        height, width = 7, 9
        images = tf.random.uniform((5, height, width, 3), seed=1)
        conv_layers.conv2d_transpose(images, 32, [3, 3], name='deconv1')
        self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)
      with tf.compat.v1.variable_scope('scope', reuse=True):
        conv_layers.conv2d_transpose(images, 32, [3, 3], name='deconv1')
        self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)

  def testFunctionalConv2DTransposeInitializerFromScope(self):
    with tf.Graph().as_default(), self.cached_session():
      with tf.compat.v1.variable_scope(
          'scope', initializer=tf.compat.v1.ones_initializer()):
        height, width = 7, 9
        images = tf.random.uniform((5, height, width, 3), seed=1)
        conv_layers.conv2d_transpose(images, 32, [3, 3], name='deconv1')
        weights = tf.compat.v1.trainable_variables()
        # Check the names of weights in order.
        self.assertTrue('kernel' in weights[0].name)
        self.assertTrue('bias' in weights[1].name)
        self.evaluate(tf.compat.v1.global_variables_initializer())
        weights = self.evaluate(weights)
        # Check that the kernel weights got initialized to ones (from scope)
        self.assertAllClose(weights[0], np.ones((3, 3, 32, 3)))
        # Check that the bias still got initialized to zeros.
        self.assertAllClose(weights[1], np.zeros((32)))

  def testFunctionalConv2DTransposeNoReuse(self):
    with tf.Graph().as_default():
      height, width = 7, 9
      images = tf.random.uniform((5, height, width, 3), seed=1)
      conv_layers.conv2d_transpose(images, 32, [3, 3])
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)
      conv_layers.conv2d_transpose(images, 32, [3, 3])
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 4)

  def testConstraints(self):
    k_constraint = lambda x: x / tf.reduce_sum(x)
    b_constraint = lambda x: x / tf.reduce_max(x)
    layer = conv_layers.Conv2DTranspose(2, 3,
                                        kernel_constraint=k_constraint,
                                        bias_constraint=b_constraint)
    inputs = tf.random.uniform((5, 3, 3, 5), seed=1)
    layer(inputs)
    self.assertEqual(layer.kernel_constraint, k_constraint)
    self.assertEqual(layer.bias_constraint, b_constraint)


class Conv3DTransposeTest(tf.test.TestCase):

  def testInvalidDataFormat(self):
    depth, height, width = 5, 7, 9
    volumes = tf.random.uniform((5, depth, height, width, 32), seed=1)
    with self.assertRaisesRegex(ValueError, 'data_format'):
      conv_layers.conv3d_transpose(volumes, 4, 3, data_format='invalid')

  def testInvalidStrides(self):
    depth, height, width = 5, 7, 9
    volumes = tf.random.uniform((5, depth, height, width, 32), seed=1)
    with self.assertRaisesRegex(ValueError, 'strides'):
      conv_layers.conv3d_transpose(volumes, 4, 3, strides=(1, 2))

    with self.assertRaisesRegex(ValueError, 'strides'):
      conv_layers.conv3d_transpose(volumes, 4, 3, strides=None)

  def testInvalidKernelSize(self):
    depth, height, width = 5, 7, 9
    volumes = tf.random.uniform((5, depth, height, width, 32), seed=1)
    with self.assertRaisesRegex(ValueError, 'kernel_size'):
      conv_layers.conv3d_transpose(volumes, 4, (1, 2))

    with self.assertRaisesRegex(ValueError, 'kernel_size'):
      conv_layers.conv3d_transpose(volumes, 4, None)

  def testCreateConv3DTranspose(self):
    depth, height, width = 5, 7, 9
    volumes = tf.random.uniform((5, depth, height, width, 32))
    layer = conv_layers.Conv3DTranspose(4, [3, 3, 3], activation=tf.nn.relu)
    output = layer.apply(volumes)
    if not tf.executing_eagerly():
      self.assertEqual(output.op.name, 'conv3d_transpose/Relu')
    self.assertListEqual(output.get_shape().as_list(),
                         [5, depth + 2, height + 2, width + 2, 4])
    self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 3, 4, 32])
    self.assertListEqual(layer.bias.get_shape().as_list(), [4])

  def testCreateConv3DTransposeIntegerKernelSize(self):
    depth, height, width = 5, 7, 9
    volumes = tf.random.uniform((5, depth, height, width, 32))
    layer = conv_layers.Conv3DTranspose(4, 3)
    output = layer.apply(volumes)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, depth + 2, height + 2, width + 2, 4])
    self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 3, 4, 32])
    self.assertListEqual(layer.bias.get_shape().as_list(), [4])

  def testCreateConv3DTransposeChannelsFirst(self):
    with tf.Graph().as_default():
      depth, height, width = 5, 7, 9
      volumes = tf.random.uniform((5, 32, depth, height, width))
      layer = conv_layers.Conv3DTranspose(
          4, [3, 3, 3], data_format='channels_first')
      output = layer.apply(volumes)
      self.assertListEqual(output.get_shape().as_list(),
                           [5, 4, depth + 2, height + 2, width + 2])
      self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 3, 4, 32])
      self.assertListEqual(layer.bias.get_shape().as_list(), [4])

  def testConv3DTransposePaddingSame(self):
    depth, height, width = 5, 7, 9
    volumes = tf.random.uniform((5, depth, height, width, 64), seed=1)
    layer = conv_layers.Conv3DTranspose(
        32, volumes.get_shape()[1:4], padding='same')
    output = layer.apply(volumes)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, depth, height, width, 32])

  def testCreateConv3DTransposeWithStrides(self):
    depth, height, width = 4, 6, 8
    # Test strides tuple.
    volumes = tf.random.uniform((5, depth, height, width, 32), seed=1)
    layer = conv_layers.Conv3DTranspose(
        4, [3, 3, 3], strides=(2, 2, 2), padding='same')
    output = layer.apply(volumes)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, depth * 2, height * 2, width * 2, 4])

    # Test strides integer.
    layer = conv_layers.Conv3DTranspose(4, [3, 3, 3], strides=2, padding='same')
    output = layer.apply(volumes)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, depth * 2, height * 2, width * 2, 4])

    # Test unequal strides.
    layer = conv_layers.Conv3DTranspose(
        4, [3, 3, 3], strides=(2, 1, 1), padding='same')
    output = layer.apply(volumes)
    self.assertListEqual(output.get_shape().as_list(),
                         [5, depth * 2, height, width, 4])

  def testConv3DTransposeKernelRegularizer(self):
    with tf.Graph().as_default():
      depth, height, width = 5, 7, 9
      volumes = tf.random.uniform((5, depth, height, width, 32))
      reg = lambda x: 0.1 * tf.reduce_sum(x)
      layer = conv_layers.Conv3DTranspose(4, [3, 3, 3], kernel_regularizer=reg)
      layer.apply(volumes)
      loss_keys = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
      self.assertEqual(len(loss_keys), 1)
      self.evaluate([v.initializer for v in layer.variables])
      self.assertListEqual(
          self.evaluate(layer.losses), self.evaluate(loss_keys))

  def testConv3DTransposeBiasRegularizer(self):
    with tf.Graph().as_default():
      depth, height, width = 5, 7, 9
      volumes = tf.random.uniform((5, depth, height, width, 32))
      reg = lambda x: 0.1 * tf.reduce_sum(x)
      layer = conv_layers.Conv3DTranspose(4, [3, 3, 3], bias_regularizer=reg)
      layer.apply(volumes)
      loss_keys = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
      self.assertEqual(len(loss_keys), 1)
      self.evaluate([v.initializer for v in layer.variables])
      self.assertListEqual(
          self.evaluate(layer.losses), self.evaluate(loss_keys))

  def testConv3DTransposeNoBias(self):
    with tf.Graph().as_default():
      depth, height, width = 5, 7, 9
      volumes = tf.random.uniform((5, depth, height, width, 32))
      layer = conv_layers.Conv3DTranspose(
          4, [3, 3, 3], activation=tf.nn.relu, use_bias=False)
      output = layer.apply(volumes)
      self.assertEqual(output.op.name, 'conv3d_transpose/Relu')
      self.assertListEqual(output.get_shape().as_list(),
                           [5, depth + 2, height + 2, width + 2, 4])
      self.assertListEqual(layer.kernel.get_shape().as_list(), [3, 3, 3, 4, 32])
      self.assertEqual(layer.bias, None)

  def testFunctionalConv3DTransposeReuse(self):
    with tf.Graph().as_default():
      depth, height, width = 5, 7, 9
      volumes = tf.random.uniform((5, depth, height, width, 32), seed=1)
      conv_layers.conv3d_transpose(volumes, 4, [3, 3, 3], name='deconv1')
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)
      conv_layers.conv3d_transpose(
          volumes, 4, [3, 3, 3], name='deconv1', reuse=True)
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)

  def testFunctionalConv3DTransposeReuseFromScope(self):
    with tf.Graph().as_default():
      with tf.compat.v1.variable_scope('scope'):
        depth, height, width = 5, 7, 9
        volumes = tf.random.uniform(
            (5, depth, height, width, 32), seed=1)
        conv_layers.conv3d_transpose(volumes, 4, [3, 3, 3], name='deconv1')
        self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)
      with tf.compat.v1.variable_scope('scope', reuse=True):
        conv_layers.conv3d_transpose(volumes, 4, [3, 3, 3], name='deconv1')
        self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)

  def testFunctionalConv3DTransposeInitializerFromScope(self):
    with tf.Graph().as_default(), self.cached_session():
      with tf.compat.v1.variable_scope(
          'scope', initializer=tf.compat.v1.ones_initializer()):
        depth, height, width = 5, 7, 9
        volumes = tf.random.uniform(
            (5, depth, height, width, 32), seed=1)
        conv_layers.conv3d_transpose(volumes, 4, [3, 3, 3], name='deconv1')
        weights = tf.compat.v1.trainable_variables()
        # Check the names of weights in order.
        self.assertTrue('kernel' in weights[0].name)
        self.assertTrue('bias' in weights[1].name)
        self.evaluate(tf.compat.v1.global_variables_initializer())
        weights = self.evaluate(weights)
        # Check that the kernel weights got initialized to ones (from scope)
        self.assertAllClose(weights[0], np.ones((3, 3, 3, 4, 32)))
        # Check that the bias still got initialized to zeros.
        self.assertAllClose(weights[1], np.zeros((4)))

  def testFunctionalConv3DTransposeNoReuse(self):
    with tf.Graph().as_default():
      depth, height, width = 5, 7, 9
      volumes = tf.random.uniform((5, depth, height, width, 32), seed=1)
      conv_layers.conv3d_transpose(volumes, 4, [3, 3, 3])
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 2)
      conv_layers.conv3d_transpose(volumes, 4, [3, 3, 3])
      self.assertEqual(len(tf.compat.v1.trainable_variables()), 4)

  def testConstraints(self):
    k_constraint = lambda x: x / tf.reduce_sum(x)
    b_constraint = lambda x: x / tf.reduce_max(x)
    layer = conv_layers.Conv3DTranspose(2, 3,
                                        kernel_constraint=k_constraint,
                                        bias_constraint=b_constraint)
    inputs = tf.random.uniform((5, 3, 3, 3, 5), seed=1)
    layer(inputs)
    self.assertEqual(layer.kernel_constraint, k_constraint)
    self.assertEqual(layer.bias_constraint, b_constraint)


if __name__ == '__main__':
  tf.test.main()
