code: # 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.
  # ==============================================================================
  """Module for constructing RNN Cells."""
  from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  
  import collections
  import math
  
  from tensorflow.contrib.compiler import jit
  from tensorflow.contrib.layers.python.layers import layers
  from tensorflow.contrib.rnn.python.ops import core_rnn_cell
  from tensorflow.python.framework import constant_op
  from tensorflow.python.framework import dtypes
  from tensorflow.python.framework import op_def_registry
  from tensorflow.python.framework import ops
  from tensorflow.python.framework import tensor_shape
  from tensorflow.python.layers import base as base_layer
  from tensorflow.python.ops import array_ops
  from tensorflow.python.ops import clip_ops
  from tensorflow.python.ops import gen_array_ops
  from tensorflow.python.ops import init_ops
  from tensorflow.python.ops import math_ops
  from tensorflow.python.ops import nn_impl  # pylint: disable=unused-import
  from tensorflow.python.ops import nn_ops
  from tensorflow.python.ops import partitioned_variables  # pylint: disable=unused-import
  from tensorflow.python.ops import random_ops
  from tensorflow.python.ops import rnn_cell_impl
  from tensorflow.python.ops import variable_scope as vs
  from tensorflow.python.platform import tf_logging as logging
  from tensorflow.python.util import nest
  
  
  def _get_concat_variable(name, shape, dtype, num_shards):
    """Get a sharded variable concatenated into one tensor."""
    sharded_variable = _get_sharded_variable(name, shape, dtype, num_shards)
    if len(sharded_variable) == 1:
      return sharded_variable[0]
  
    concat_name = name + "/concat"
    concat_full_name = vs.get_variable_scope().name + "/" + concat_name + ":0"
    for value in ops.get_collection(ops.GraphKeys.CONCATENATED_VARIABLES):
      if value.name == concat_full_name:
        return value
  
    concat_variable = array_ops.concat(sharded_variable, 0, name=concat_name)
    ops.add_to_collection(ops.GraphKeys.CONCATENATED_VARIABLES, concat_variable)
    return concat_variable
  
  
  def _get_sharded_variable(name, shape, dtype, num_shards):
    """Get a list of sharded variables with the given dtype."""
    if num_shards > shape[0]:
      raise ValueError("Too many shards: shape=%s, num_shards=%d" % (shape,
                                                                     num_shards))
    unit_shard_size = int(math.floor(shape[0] / num_shards))
    remaining_rows = shape[0] - unit_shard_size * num_shards
  
    shards = []
    for i in range(num_shards):
      current_size = unit_shard_size
      if i < remaining_rows:
        current_size += 1
      shards.append(
          vs.get_variable(
              name + "_%d" % i, [current_size] + shape[1:], dtype=dtype))
    return shards
  
  
  def _norm(g, b, inp, scope):
    shape = inp.get_shape()[-1:]
    gamma_init = init_ops.constant_initializer(g)
    beta_init = init_ops.constant_initializer(b)
    with vs.variable_scope(scope):
      # Initialize beta and gamma for use by layer_norm.
      vs.get_variable("gamma", shape=shape, initializer=gamma_init)
      vs.get_variable("beta", shape=shape, initializer=beta_init)
    normalized = layers.layer_norm(inp, reuse=True, scope=scope)
    return normalized
  
  
  class CoupledInputForgetGateLSTMCell(rnn_cell_impl.RNNCell):
    """Long short-term memory unit (LSTM) recurrent network cell.
  
    The default non-peephole implementation is based on:
  
      http://www.bioinf.jku.at/publications/older/2604.pdf
  
    S. Hochreiter and J. Schmidhuber.
    "Long Short-Term Memory". Neural Computation, 9(8):1735-1780, 1997.
  
    The peephole implementation is based on:
  
      https://research.google.com/pubs/archive/43905.pdf
  
    Hasim Sak, Andrew Senior, and Francoise Beaufays.
    "Long short-term memory recurrent neural network architectures for
     large scale acoustic modeling." INTERSPEECH, 2014.
  
    The coupling of input and forget gate is based on:
  
      http://arxiv.org/pdf/1503.04069.pdf
  
    Greff et al. "LSTM: A Search Space Odyssey"
  
    The class uses optional peep-hole connections, and an optional projection
    layer.
    Layer normalization implementation is based on:
  
      https://arxiv.org/abs/1607.06450.
  
    "Layer Normalization"
    Jimmy Lei Ba, Jamie Ryan Kiros, Geoffrey E. Hinton
  
    and is applied before the internal nonlinearities.
  
    """
  
    def __init__(self,
                 num_units,
                 use_peepholes=False,
                 initializer=None,
                 num_proj=None,
                 proj_clip=None,
                 num_unit_shards=1,
                 num_proj_shards=1,
                 forget_bias=1.0,
                 state_is_tuple=True,
                 activation=math_ops.tanh,
                 reuse=None,
                 layer_norm=False,
                 norm_gain=1.0,
                 norm_shift=0.0):
      """Initialize the parameters for an LSTM cell.
  
      Args:
        num_units: int, The number of units in the LSTM cell
        use_peepholes: bool, set True to enable diagonal/peephole connections.
        initializer: (optional) The initializer to use for the weight and
          projection matrices.
        num_proj: (optional) int, The output dimensionality for the projection
          matrices.  If None, no projection is performed.
        proj_clip: (optional) A float value.  If `num_proj > 0` and `proj_clip` is
        provided, then the projected values are clipped elementwise to within
        `[-proj_clip, proj_clip]`.
        num_unit_shards: How to split the weight matrix.  If >1, the weight
          matrix is stored across num_unit_shards.
        num_proj_shards: How to split the projection matrix.  If >1, the
          projection matrix is stored across num_proj_shards.
        forget_bias: Biases of the forget gate are initialized by default to 1
          in order to reduce the scale of forgetting at the beginning of
          the training.
        state_is_tuple: If True, accepted and returned states are 2-tuples of
          the `c_state` and `m_state`.  By default (False), they are concatenated
          along the column axis.  This default behavior will soon be deprecated.
        activation: Activation function of the inner states.
        reuse: (optional) Python boolean describing whether to reuse variables
          in an existing scope.  If not `True`, and the existing scope already has
          the given variables, an error is raised.
        layer_norm: If `True`, layer normalization will be applied.
        norm_gain: float, The layer normalization gain initial value. If
          `layer_norm` has been set to `False`, this argument will be ignored.
        norm_shift: float, The layer normalization shift initial value. If
          `layer_norm` has been set to `False`, this argument will be ignored.
      """
      super(CoupledInputForgetGateLSTMCell, self).__init__(_reuse=reuse)
      if not state_is_tuple:
        logging.warn("%s: Using a concatenated state is slower and will soon be "
                     "deprecated.  Use state_is_tuple=True.", self)
      self._num_units = num_units
      self._use_peepholes = use_peepholes
      self._initializer = initializer
      self._num_proj = num_proj
      self._proj_clip = proj_clip
      self._num_unit_shards = num_unit_shards
      self._num_proj_shards = num_proj_shards
      self._forget_bias = forget_bias
      self._state_is_tuple = state_is_tuple
      self._activation = activation
      self._reuse = reuse
      self._layer_norm = layer_norm
      self._norm_gain = norm_gain
      self._norm_shift = norm_shift
  
      if num_proj:
        self._state_size = (
            rnn_cell_impl.LSTMStateTuple(num_units, num_proj)
            if state_is_tuple else num_units + num_proj)
        self._output_size = num_proj
      else:
        self._state_size = (
            rnn_cell_impl.LSTMStateTuple(num_units, num_units)
            if state_is_tuple else 2 * num_units)
        self._output_size = num_units
  
    @property
    def state_size(self):
      return self._state_size
  
    @property
    def output_size(self):
      return self._output_size
  
    def call(self, inputs, state):
      """Run one step of LSTM.
  
      Args:
        inputs: input Tensor, 2D, batch x num_units.
        state: if `state_is_tuple` is False, this must be a state Tensor,
          `2-D, batch x state_size`.  If `state_is_tuple` is True, this must be a
          tuple of state Tensors, both `2-D`, with column sizes `c_state` and
          `m_state`.
  
      Returns:
        A tuple containing:
        - A `2-D, [batch x output_dim]`, Tensor representing the output of the
          LSTM after reading `inputs` when previous state was `state`.
          Here output_dim is:
             num_proj if num_proj was set,
             num_units otherwise.
        - Tensor(s) representing the new state of LSTM after reading `inputs` when
          the previous state was `state`.  Same type and shape(s) as `state`.
  
      Raises:
        ValueError: If input size cannot be inferred from inputs via
          static shape inference.
      """
      sigmoid = math_ops.sigmoid
  
      num_proj = self._num_units if self._num_proj is None else self._num_proj
  
      if self._state_is_tuple:
        (c_prev, m_prev) = state
      else:
        c_prev = array_ops.slice(state, [0, 0], [-1, self._num_units])
        m_prev = array_ops.slice(state, [0, self._num_units], [-1, num_proj])
  
      dtype = inputs.dtype
      input_size = inputs.get_shape().with_rank(2)[1]
      if input_size.value is None:
        raise ValueError("Could not infer input size from inputs.get_shape()[-1]")
      concat_w = _get_concat_variable(
          "W", [input_size.value + num_proj, 3 * self._num_units], dtype,
          self._num_unit_shards)
  
      b = vs.get_variable(
          "B",
          shape=[3 * self._num_units],
          initializer=init_ops.zeros_initializer(),
          dtype=dtype)
  
      # j = new_input, f = forget_gate, o = output_gate
      cell_inputs = array_ops.concat([inputs, m_prev], 1)
      lstm_matrix = math_ops.matmul(cell_inputs, concat_w)
  
      # If layer nomalization is applied, do not add bias
      if not self._layer_norm:
        lstm_matrix = nn_ops.bias_add(lstm_matrix, b)
  
      j, f, o = array_ops.split(value=lstm_matrix, num_or_size_splits=3, axis=1)
  
      # Apply layer normalization
      if self._layer_norm:
        j = _norm(self._norm_gain, self._norm_shift, j, "transform")
        f = _norm(self._norm_gain, self._norm_shift, f, "forget")
        o = _norm(self._norm_gain, self._norm_shift, o, "output")
  
      # Diagonal connections
      if self._use_peepholes:
        w_f_diag = vs.get_variable(
            "W_F_diag", shape=[self._num_units], dtype=dtype)
        w_o_diag = vs.get_variable(
            "W_O_diag", shape=[self._num_units], dtype=dtype)
  
      if self._use_peepholes:
        f_act = sigmoid(f + self._forget_bias + w_f_diag * c_prev)
      else:
        f_act = sigmoid(f + self._forget_bias)
      c = (f_act * c_prev + (1 - f_act) * self._activation(j))
  
      # Apply layer normalization
      if self._layer_norm:
        c = _norm(self._norm_gain, self._norm_shift, c, "state")
  
      if self._use_peepholes:
        m = sigmoid(o + w_o_diag * c) * self._activation(c)
      else:
        m = sigmoid(o) * self._activation(c)
  
      if self._num_proj is not None:
        concat_w_proj = _get_concat_variable("W_P",
                                             [self._num_units, self._num_proj],
                                             dtype, self._num_proj_shards)
  
        m = math_ops.matmul(m, concat_w_proj)
        if self._proj_clip is not None:
          # pylint: disable=invalid-unary-operand-type
          m = clip_ops.clip_by_value(m, -self._proj_clip, self._proj_clip)
          # pylint: enable=invalid-unary-operand-type
  
      new_state = (
          rnn_cell_impl.LSTMStateTuple(c, m)
          if self._state_is_tuple else array_ops.concat([c, m], 1))
      return m, new_state
  
  
  class TimeFreqLSTMCell(rnn_cell_impl.RNNCell):
    """Time-Frequency Long short-term memory unit (LSTM) recurrent network cell.
  
    This implementation is based on:
  
      Tara N. Sainath and Bo Li
      "Modeling Time-Frequency Patterns with LSTM vs. Convolutional Architectures
      for LVCSR Tasks." submitted to INTERSPEECH, 2016.
  
    It uses peep-hole connections and optional cell clipping.
    """
  
    def __init__(self,
                 num_units,
                 use_peepholes=False,
                 cell_clip=None,
                 initializer=None,
                 num_unit_shards=1,
                 forget_bias=1.0,
                 feature_size=None,
                 frequency_skip=1,
                 reuse=None):
      """Initialize the parameters for an LSTM cell.
  
      Args:
        num_units: int, The number of units in the LSTM cell
        use_peepholes: bool, set True to enable diagonal/peephole connections.
        cell_clip: (optional) A float value, if provided the cell state is clipped
          by this value prior to the cell output activation.
        initializer: (optional) The initializer to use for the weight and
          projection matrices.
        num_unit_shards: int, How to split the weight matrix.  If >1, the weight
          matrix is stored across num_unit_shards.
        forget_bias: float, Biases of the forget gate are initialized by default
          to 1 in order to reduce the scale of forgetting at the beginning
          of the training.
        feature_size: int, The size of the input feature the LSTM spans over.
        frequency_skip: int, The amount the LSTM filter is shifted by in
          frequency.
        reuse: (optional) Python boolean describing whether to reuse variables
          in an existing scope.  If not `True`, and the existing scope already has
          the given variables, an error is raised.
      """
      super(TimeFreqLSTMCell, self).__init__(_reuse=reuse)
      self._num_units = num_units
      self._use_peepholes = use_peepholes
      self._cell_clip = cell_clip
      self._initializer = initializer
      self._num_unit_shards = num_unit_shards
      self._forget_bias = forget_bias
      self._feature_size = feature_size
      self._frequency_skip = frequency_skip
      self._state_size = 2 * num_units
      self._output_size = num_units
      self._reuse = reuse
  
    @property
    def output_size(self):
      return self._output_size
  
    @property
    def state_size(self):
      return self._state_size
  
    def call(self, inputs, state):
      """Run one step of LSTM.
  
      Args:
        inputs: input Tensor, 2D, batch x num_units.
        state: state Tensor, 2D, batch x state_size.
  
      Returns:
        A tuple containing:
        - A 2D, batch x output_dim, Tensor representing the output of the LSTM
          after reading "inputs" when previous state was "state".
          Here output_dim is num_units.
        - A 2D, batch x state_size, Tensor representing the new state of LSTM
          after reading "inputs" when previous state was "state".
      Raises:
        ValueError: if an input_size was specified and the provided inputs have
          a different dimension.
      """
      sigmoid = math_ops.sigmoid
      tanh = math_ops.tanh
  
      freq_inputs = self._make_tf_features(inputs)
      dtype = inputs.dtype
      actual_input_size = freq_inputs[0].get_shape().as_list()[1]
  
      concat_w = _get_concat_variable(
          "W", [actual_input_size + 2 * self._num_units, 4 * self._num_units],
          dtype, self._num_unit_shards)
  
      b = vs.get_variable(
          "B",
          shape=[4 * self._num_units],
          initializer=init_ops.zeros_initializer(),
          dtype=dtype)
  
      # Diagonal connections
      if self._use_peepholes:
        w_f_diag = vs.get_variable(
            "W_F_diag", shape=[self._num_units], dtype=dtype)
        w_i_diag = vs.get_variable(
            "W_I_diag", shape=[self._num_units], dtype=dtype)
        w_o_diag = vs.get_variable(
            "W_O_diag", shape=[self._num_units], dtype=dtype)
  
      # initialize the first freq state to be zero
      m_prev_freq = array_ops.zeros(
          [inputs.shape[0].value or inputs.get_shape()[0], self._num_units],
          dtype)
      for fq in range(len(freq_inputs)):
        c_prev = array_ops.slice(state, [0, 2 * fq * self._num_units],
                                 [-1, self._num_units])
        m_prev = array_ops.slice(state, [0, (2 * fq + 1) * self._num_units],
                                 [-1, self._num_units])
        # i = input_gate, j = new_input, f = forget_gate, o = output_gate
        cell_inputs = array_ops.concat([freq_inputs[fq], m_prev, m_prev_freq], 1)
        lstm_matrix = nn_ops.bias_add(math_ops.matmul(cell_inputs, concat_w), b)
        i, j, f, o = array_ops.split(
            value=lstm_matrix, num_or_size_splits=4, axis=1)
  
        if self._use_peepholes:
          c = (
              sigmoid(f + self._forget_bias + w_f_diag * c_prev) * c_prev +
              sigmoid(i + w_i_diag * c_prev) * tanh(j))
        else:
          c = (sigmoid(f + self._forget_bias) * c_prev + sigmoid(i) * tanh(j))
  
        if self._cell_clip is not None:
          # pylint: disable=invalid-unary-operand-type
          c = clip_ops.clip_by_value(c, -self._cell_clip, self._cell_clip)
          # pylint: enable=invalid-unary-operand-type
  
        if self._use_peepholes:
          m = sigmoid(o + w_o_diag * c) * tanh(c)
        else:
          m = sigmoid(o) * tanh(c)
        m_prev_freq = m
        if fq == 0:
          state_out = array_ops.concat([c, m], 1)
          m_out = m
        else:
          state_out = array_ops.concat([state_out, c, m], 1)
          m_out = array_ops.concat([m_out, m], 1)
      return m_out, state_out
  
    def _make_tf_features(self, input_feat):
      """Make the frequency features.
  
      Args:
        input_feat: input Tensor, 2D, batch x num_units.
  
      Returns:
        A list of frequency features, with each element containing:
        - A 2D, batch x output_dim, Tensor representing the time-frequency feature
          for that frequency index. Here output_dim is feature_size.
      Raises:
        ValueError: if input_size cannot be inferred from static shape inference.
      """
      input_size = input_feat.get_shape().with_rank(2)[-1].value
      if input_size is None:
        raise ValueError("Cannot infer input_size from static shape inference.")
      num_feats = int(
          (input_size - self._feature_size) / (self._frequency_skip)) + 1
      freq_inputs = []
      for f in range(num_feats):
        cur_input = array_ops.slice(input_feat, [0, f * self._frequency_skip],
                                    [-1, self._feature_size])
        freq_inputs.append(cur_input)
      return freq_inputs
  
  
  class GridLSTMCell(rnn_cell_impl.RNNCell):
    """Grid Long short-term memory unit (LSTM) recurrent network cell.
  
    The default is based on:
      Nal Kalchbrenner, Ivo Danihelka and Alex Graves
      "Grid Long Short-Term Memory," Proc. ICLR 2016.
      http://arxiv.org/abs/1507.01526
  
    When peephole connections are used, the implementation is based on:
      Tara N. Sainath and Bo Li
      "Modeling Time-Frequency Patterns with LSTM vs. Convolutional Architectures
      for LVCSR Tasks." submitted to INTERSPEECH, 2016.
  
    The code uses optional peephole connections, shared_weights and cell clipping.
    """
  
    def __init__(self,
                 num_units,
                 use_peepholes=False,
                 share_time_frequency_weights=False,
                 cell_clip=None,
                 initializer=None,
                 num_unit_shards=1,
                 forget_bias=1.0,
                 feature_size=None,
                 frequency_skip=None,
                 num_frequency_blocks=None,
                 start_freqindex_list=None,
                 end_freqindex_list=None,
                 couple_input_forget_gates=False,
                 state_is_tuple=True,
                 reuse=None):
      """Initialize the parameters for an LSTM cell.
  
      Args:
        num_units: int, The number of units in the LSTM cell
        use_peepholes: (optional) bool, default False. Set True to enable
          diagonal/peephole connections.
        share_time_frequency_weights: (optional) bool, default False. Set True to
          enable shared cell weights between time and frequency LSTMs.
        cell_clip: (optional) A float value, default None, if provided the cell
          state is clipped by this value prior to the cell output activation.
        initializer: (optional) The initializer to use for the weight and
          projection matrices, default None.
        num_unit_shards: (optional) int, default 1, How to split the weight
          matrix. If > 1, the weight matrix is stored across num_unit_shards.
        forget_bias: (optional) float, default 1.0, The initial bias of the
          forget gates, used to reduce the scale of forgetting at the beginning
          of the training.
        feature_size: (optional) int, default None, The size of the input feature
          the LSTM spans over.
        frequency_skip: (optional) int, default None, The amount the LSTM filter
          is shifted by in frequency.
        num_frequency_blocks: [required] A list of frequency blocks needed to
          cover the whole input feature splitting defined by start_freqindex_list
          and end_freqindex_list.
        start_freqindex_list: [optional], list of ints, default None,  The
          starting frequency index for each frequency block.
        end_freqindex_list: [optional], list of ints, default None. The ending
          frequency index for each frequency block.
        couple_input_forget_gates: (optional) bool, default False, Whether to
          couple the input and forget gates, i.e. f_gate = 1.0 - i_gate, to reduce
          model parameters and computation cost.
        state_is_tuple: If True, accepted and returned states are 2-tuples of
          the `c_state` and `m_state`.  By default (False), they are concatenated
          along the column axis.  This default behavior will soon be deprecated.
        reuse: (optional) Python boolean describing whether to reuse variables
          in an existing scope.  If not `True`, and the existing scope already has
          the given variables, an error is raised.
      Raises:
        ValueError: if the num_frequency_blocks list is not specified
      """
      super(GridLSTMCell, self).__init__(_reuse=reuse)
      if not state_is_tuple:
        logging.warn("%s: Using a concatenated state is slower and will soon be "
                     "deprecated.  Use state_is_tuple=True.", self)
      self._num_units = num_units
      self._use_peepholes = use_peepholes
      self._share_time_frequency_weights = share_time_frequency_weights
      self._couple_input_forget_gates = couple_input_forget_gates
      self._state_is_tuple = state_is_tuple
      self._cell_clip = cell_clip
      self._initializer = initializer
      self._num_unit_shards = num_unit_shards
      self._forget_bias = forget_bias
      self._feature_size = feature_size
      self._frequency_skip = frequency_skip
      self._start_freqindex_list = start_freqindex_list
      self._end_freqindex_list = end_freqindex_list
      self._num_frequency_blocks = num_frequency_blocks
      self._total_blocks = 0
      self._reuse = reuse
      if self._num_frequency_blocks is None:
        raise ValueError("Must specify num_frequency_blocks")
  
      for block_index in range(len(self._num_frequency_blocks)):
        self._total_blocks += int(self._num_frequency_blocks[block_index])
      if state_is_tuple:
        state_names = ""
        for block_index in range(len(self._num_frequency_blocks)):
          for freq_index in range(self._num_frequency_blocks[block_index]):
            name_prefix = "state_f%02d_b%02d" % (freq_index, block_index)
            state_names += ("%s_c, %s_m," % (name_prefix, name_prefix))
        self._state_tuple_type = collections.namedtuple("GridLSTMStateTuple",
                                                        state_names.strip(","))
        self._state_size = self._state_tuple_type(*(
            [num_units, num_units] * self._total_blocks))
      else:
        self._state_tuple_type = None
        self._state_size = num_units * self._total_blocks * 2
      self._output_size = num_units * self._total_blocks * 2
  
    @property
    def output_size(self):
      return self._output_size
  
    @property
    def state_size(self):
      return self._state_size
  
    @property
    def state_tuple_type(self):
      return self._state_tuple_type
  
    def call(self, inputs, state):
      """Run one step of LSTM.
  
      Args:
        inputs: input Tensor, 2D, [batch, feature_size].
        state: Tensor or tuple of Tensors, 2D, [batch, state_size], depends on the
          flag self._state_is_tuple.
  
      Returns:
        A tuple containing:
        - A 2D, [batch, output_dim], Tensor representing the output of the LSTM
          after reading "inputs" when previous state was "state".
          Here output_dim is num_units.
        - A 2D, [batch, state_size], Tensor representing the new state of LSTM
          after reading "inputs" when previous state was "state".
      Raises:
        ValueError: if an input_size was specified and the provided inputs have
          a different dimension.
      """
      batch_size = inputs.shape[0].value or array_ops.shape(inputs)[0]
      freq_inputs = self._make_tf_features(inputs)
      m_out_lst = []
      state_out_lst = []
      for block in range(len(freq_inputs)):
        m_out_lst_current, state_out_lst_current = self._compute(
            freq_inputs[block],
            block,
            state,
            batch_size,
            state_is_tuple=self._state_is_tuple)
        m_out_lst.extend(m_out_lst_current)
        state_out_lst.extend(state_out_lst_current)
      if self._state_is_tuple:
        state_out = self._state_tuple_type(*state_out_lst)
      else:
        state_out = array_ops.concat(state_out_lst, 1)
      m_out = array_ops.concat(m_out_lst, 1)
      return m_out, state_out
  
    def _compute(self,
                 freq_inputs,
                 block,
                 state,
                 batch_size,
                 state_prefix="state",
                 state_is_tuple=True):
      """Run the actual computation of one step LSTM.
  
      Args:
        freq_inputs: list of Tensors, 2D, [batch, feature_size].
        block: int, current frequency block index to process.
        state: Tensor or tuple of Tensors, 2D, [batch, state_size], it depends on
          the flag state_is_tuple.
        batch_size: int32, batch size.
        state_prefix: (optional) string, name prefix for states, defaults to
          "state".
        state_is_tuple: boolean, indicates whether the state is a tuple or Tensor.
  
      Returns:
        A tuple, containing:
        - A list of [batch, output_dim] Tensors, representing the output of the
          LSTM given the inputs and state.
        - A list of [batch, state_size] Tensors, representing the LSTM state
          values given the inputs and previous state.
      """
      sigmoid = math_ops.sigmoid
      tanh = math_ops.tanh
      num_gates = 3 if self._couple_input_forget_gates else 4
      dtype = freq_inputs[0].dtype
      actual_input_size = freq_inputs[0].get_shape().as_list()[1]
  
      concat_w_f = _get_concat_variable(
          "W_f_%d" % block,
          [actual_input_size + 2 * self._num_units, num_gates * self._num_units],
          dtype, self._num_unit_shards)
      b_f = vs.get_variable(
          "B_f_%d" % block,
          shape=[num_gates * self._num_units],
          initializer=init_ops.zeros_initializer(),
          dtype=dtype)
      if not self._share_time_frequency_weights:
        concat_w_t = _get_concat_variable("W_t_%d" % block, [
            actual_input_size + 2 * self._num_units, num_gates * self._num_units
        ], dtype, self._num_unit_shards)
        b_t = vs.get_variable(
            "B_t_%d" % block,
            shape=[num_gates * self._num_units],
            initializer=init_ops.zeros_initializer(),
            dtype=dtype)
  
      if self._use_peepholes:
        # Diagonal connections
        if not self._couple_input_forget_gates:
          w_f_diag_freqf = vs.get_variable(
              "W_F_diag_freqf_%d" % block, shape=[self._num_units], dtype=dtype)
          w_f_diag_freqt = vs.get_variable(
              "W_F_diag_freqt_%d" % block, shape=[self._num_units], dtype=dtype)
        w_i_diag_freqf = vs.get_variable(
            "W_I_diag_freqf_%d" % block, shape=[self._num_units], dtype=dtype)
        w_i_diag_freqt = vs.get_variable(
            "W_I_diag_freqt_%d" % block, shape=[self._num_units], dtype=dtype)
        w_o_diag_freqf = vs.get_variable(
            "W_O_diag_freqf_%d" % block, shape=[self._num_units], dtype=dtype)
        w_o_diag_freqt = vs.get_variable(
            "W_O_diag_freqt_%d" % block, shape=[self._num_units], dtype=dtype)
        if not self._share_time_frequency_weights:
          if not self._couple_input_forget_gates:
            w_f_diag_timef = vs.get_variable(
                "W_F_diag_timef_%d" % block, shape=[self._num_units], dtype=dtype)
            w_f_diag_timet = vs.get_variable(
                "W_F_diag_timet_%d" % block, shape=[self._num_units], dtype=dtype)
          w_i_diag_timef = vs.get_variable(
              "W_I_diag_timef_%d" % block, shape=[self._num_units], dtype=dtype)
          w_i_diag_timet = vs.get_variable(
              "W_I_diag_timet_%d" % block, shape=[self._num_units], dtype=dtype)
          w_o_diag_timef = vs.get_variable(
              "W_O_diag_timef_%d" % block, shape=[self._num_units], dtype=dtype)
          w_o_diag_timet = vs.get_variable(
              "W_O_diag_timet_%d" % block, shape=[self._num_units], dtype=dtype)
  
      # initialize the first freq state to be zero
      m_prev_freq = array_ops.zeros([batch_size, self._num_units], dtype)
      c_prev_freq = array_ops.zeros([batch_size, self._num_units], dtype)
      for freq_index in range(len(freq_inputs)):
        if state_is_tuple:
          name_prefix = "%s_f%02d_b%02d" % (state_prefix, freq_index, block)
          c_prev_time = getattr(state, name_prefix + "_c")
          m_prev_time = getattr(state, name_prefix + "_m")
        else:
          c_prev_time = array_ops.slice(
              state, [0, 2 * freq_index * self._num_units], [-1, self._num_units])
          m_prev_time = array_ops.slice(
              state, [0, (2 * freq_index + 1) * self._num_units],
              [-1, self._num_units])
  
        # i = input_gate, j = new_input, f = forget_gate, o = output_gate
        cell_inputs = array_ops.concat(
            [freq_inputs[freq_index], m_prev_time, m_prev_freq], 1)
  
        # F-LSTM
        lstm_matrix_freq = nn_ops.bias_add(
            math_ops.matmul(cell_inputs, concat_w_f), b_f)
        if self._couple_input_forget_gates:
          i_freq, j_freq, o_freq = array_ops.split(
              value=lstm_matrix_freq, num_or_size_splits=num_gates, axis=1)
          f_freq = None
        else:
          i_freq, j_freq, f_freq, o_freq = array_ops.split(
              value=lstm_matrix_freq, num_or_size_splits=num_gates, axis=1)
        # T-LSTM
        if self._share_time_frequency_weights:
          i_time = i_freq
          j_time = j_freq
          f_time = f_freq
          o_time = o_freq
        else:
          lstm_matrix_time = nn_ops.bias_add(
              math_ops.matmul(cell_inputs, concat_w_t), b_t)
          if self._couple_input_forget_gates:
            i_time, j_time, o_time = array_ops.split(
                value=lstm_matrix_time, num_or_size_splits=num_gates, axis=1)
            f_time = None
          else:
            i_time, j_time, f_time, o_time = array_ops.split(
                value=lstm_matrix_time, num_or_size_splits=num_gates, axis=1)
  
        # F-LSTM c_freq
        # input gate activations
        if self._use_peepholes:
          i_freq_g = sigmoid(i_freq + w_i_diag_freqf * c_prev_freq +
                             w_i_diag_freqt * c_prev_time)
        else:
          i_freq_g = sigmoid(i_freq)
        # forget gate activations
        if self._couple_input_forget_gates:
          f_freq_g = 1.0 - i_freq_g
        else:
          if self._use_peepholes:
            f_freq_g = sigmoid(f_freq + self._forget_bias + w_f_diag_freqf *
                               c_prev_freq + w_f_diag_freqt * c_prev_time)
          else:
            f_freq_g = sigmoid(f_freq + self._forget_bias)
        # cell state
        c_freq = f_freq_g * c_prev_freq + i_freq_g * tanh(j_freq)
        if self._cell_clip is not None:
          # pylint: disable=invalid-unary-operand-type
          c_freq = clip_ops.clip_by_value(c_freq, -self._cell_clip,
                                          self._cell_clip)
          # pylint: enable=invalid-unary-operand-type
  
        # T-LSTM c_freq
        # input gate activations
        if self._use_peepholes:
          if self._share_time_frequency_weights:
            i_time_g = sigmoid(i_time + w_i_diag_freqf * c_prev_freq +
                               w_i_diag_freqt * c_prev_time)
          else:
            i_time_g = sigmoid(i_time + w_i_diag_timef * c_prev_freq +
                               w_i_diag_timet * c_prev_time)
        else:
          i_time_g = sigmoid(i_time)
        # forget gate activations
        if self._couple_input_forget_gates:
          f_time_g = 1.0 - i_time_g
        else:
          if self._use_peepholes:
            if self._share_time_frequency_weights:
              f_time_g = sigmoid(f_time + self._forget_bias + w_f_diag_freqf *
                                 c_prev_freq + w_f_diag_freqt * c_prev_time)
            else:
              f_time_g = sigmoid(f_time + self._forget_bias + w_f_diag_timef *
                                 c_prev_freq + w_f_diag_timet * c_prev_time)
          else:
            f_time_g = sigmoid(f_time + self._forget_bias)
        # cell state
        c_time = f_time_g * c_prev_time + i_time_g * tanh(j_time)
        if self._cell_clip is not None:
          # pylint: disable=invalid-unary-operand-type
          c_time = clip_ops.clip_by_value(c_time, -self._cell_clip,
                                          self._cell_clip)
          # pylint: enable=invalid-unary-operand-type
  
        # F-LSTM m_freq
        if self._use_peepholes:
          m_freq = sigmoid(o_freq + w_o_diag_freqf * c_freq +
                           w_o_diag_freqt * c_time) * tanh(c_freq)
        else:
          m_freq = sigmoid(o_freq) * tanh(c_freq)
  
        # T-LSTM m_time
        if self._use_peepholes:
          if self._share_time_frequency_weights:
            m_time = sigmoid(o_time + w_o_diag_freqf * c_freq +
                             w_o_diag_freqt * c_time) * tanh(c_time)
          else:
            m_time = sigmoid(o_time + w_o_diag_timef * c_freq +
                             w_o_diag_timet * c_time) * tanh(c_time)
        else:
          m_time = sigmoid(o_time) * tanh(c_time)
  
        m_prev_freq = m_freq
        c_prev_freq = c_freq
        # Concatenate the outputs for T-LSTM and F-LSTM for each shift
        if freq_index == 0:
          state_out_lst = [c_time, m_time]
          m_out_lst = [m_time, m_freq]
        else:
          state_out_lst.extend([c_time, m_time])
          m_out_lst.extend([m_time, m_freq])
  
      return m_out_lst, state_out_lst
  
    def _make_tf_features(self, input_feat, slice_offset=0):
      """Make the frequency features.
  
      Args:
        input_feat: input Tensor, 2D, [batch, num_units].
        slice_offset: (optional) Python int, default 0, the slicing offset is only
          used for the backward processing in the BidirectionalGridLSTMCell. It
          specifies a different starting point instead of always 0 to enable the
          forward and backward processing look at different frequency blocks.
  
      Returns:
        A list of frequency features, with each element containing:
        - A 2D, [batch, output_dim], Tensor representing the time-frequency
          feature for that frequency index. Here output_dim is feature_size.
      Raises:
        ValueError: if input_size cannot be inferred from static shape inference.
      """
      input_size = input_feat.get_shape().with_rank(2)[-1].value
      if input_size is None:
        raise ValueError("Cannot infer input_size from static shape inference.")
      if slice_offset > 0:
        # Padding to the end
        inputs = array_ops.pad(input_feat,
                               array_ops.constant(
                                   [0, 0, 0, slice_offset],
                                   shape=[2, 2],
                                   dtype=dtypes.int32), "CONSTANT")
      elif slice_offset < 0:
        # Padding to the front
        inputs = array_ops.pad(input_feat,
                               array_ops.constant(
                                   [0, 0, -slice_offset, 0],
                                   shape=[2, 2],
                                   dtype=dtypes.int32), "CONSTANT")
        slice_offset = 0
      else:
        inputs = input_feat
      freq_inputs = []
      if not self._start_freqindex_list:
        if len(self._num_frequency_blocks) != 1:
          raise ValueError("Length of num_frequency_blocks"
                           " is not 1, but instead is %d",
                           len(self._num_frequency_blocks))
        num_feats = int(
            (input_size - self._feature_size) / (self._frequency_skip)) + 1
        if num_feats != self._num_frequency_blocks[0]:
          raise ValueError(
              "Invalid num_frequency_blocks, requires %d but gets %d, please"
              " check the input size and filter config are correct." %
              (self._num_frequency_blocks[0], num_feats))
        block_inputs = []
        for f in range(num_feats):
          cur_input = array_ops.slice(
              inputs, [0, slice_offset + f * self._frequency_skip],
              [-1, self._feature_size])
          block_inputs.append(cur_input)
        freq_inputs.append(block_inputs)
      else:
        if len(self._start_freqindex_list) != len(self._end_freqindex_list):
          raise ValueError("Length of start and end freqindex_list"
                           " does not match %d %d",
                           len(self._start_freqindex_list),
                           len(self._end_freqindex_list))
        if len(self._num_frequency_blocks) != len(self._start_freqindex_list):
          raise ValueError("Length of num_frequency_blocks"
                           " is not equal to start_freqindex_list %d %d",
                           len(self._num_frequency_blocks),
                           len(self._start_freqindex_list))
        for b in range(len(self._start_freqindex_list)):
          start_index = self._start_freqindex_list[b]
          end_index = self._end_freqindex_list[b]
          cur_size = end_index - start_index
          block_feats = int(
              (cur_size - self._feature_size) / (self._frequency_skip)) + 1
          if block_feats != self._num_frequency_blocks[b]:
            raise ValueError(
                "Invalid num_frequency_blocks, requires %d but gets %d, please"
                " check the input size and filter config are correct." %
                (self._num_frequency_blocks[b], block_feats))
          block_inputs = []
          for f in range(block_feats):
            cur_input = array_ops.slice(
                inputs,
                [0, start_index + slice_offset + f * self._frequency_skip],
                [-1, self._feature_size])
            block_inputs.append(cur_input)
          freq_inputs.append(block_inputs)
      return freq_inputs
  
  
  class BidirectionalGridLSTMCell(GridLSTMCell):
    """Bidirectional GridLstm cell.
  
    The bidirection connection is only used in the frequency direction, which
    hence doesn't affect the time direction's real-time processing that is
    required for online recognition systems.
    The current implementation uses different weights for the two directions.
    """
  
    def __init__(self,
                 num_units,
                 use_peepholes=False,
                 share_time_frequency_weights=False,
                 cell_clip=None,
                 initializer=None,
                 num_unit_shards=1,
                 forget_bias=1.0,
                 feature_size=None,
                 frequency_skip=None,
                 num_frequency_blocks=None,
                 start_freqindex_list=None,
                 end_freqindex_list=None,
                 couple_input_forget_gates=False,
                 backward_slice_offset=0,
                 reuse=None):
      """Initialize the parameters for an LSTM cell.
  
      Args:
        num_units: int, The number of units in the LSTM cell
        use_peepholes: (optional) bool, default False. Set True to enable
          diagonal/peephole connections.
        share_time_frequency_weights: (optional) bool, default False. Set True to
          enable shared cell weights between time and frequency LSTMs.
        cell_clip: (optional) A float value, default None, if provided the cell
          state is clipped by this value prior to the cell output activation.
        initializer: (optional) The initializer to use for the weight and
          projection matrices, default None.
        num_unit_shards: (optional) int, default 1, How to split the weight
          matrix. If > 1, the weight matrix is stored across num_unit_shards.
        forget_bias: (optional) float, default 1.0, The initial bias of the
          forget gates, used to reduce the scale of forgetting at the beginning
          of the training.
        feature_size: (optional) int, default None, The size of the input feature
          the LSTM spans over.
        frequency_skip: (optional) int, default None, The amount the LSTM filter
          is shifted by in frequency.
        num_frequency_blocks: [required] A list of frequency blocks needed to
          cover the whole input feature splitting defined by start_freqindex_list
          and end_freqindex_list.
        start_freqindex_list: [optional], list of ints, default None,  The
          starting frequency index for each frequency block.
        end_freqindex_list: [optional], list of ints, default None. The ending
          frequency index for each frequency block.
        couple_input_forget_gates: (optional) bool, default False, Whether to
          couple the input and forget gates, i.e. f_gate = 1.0 - i_gate, to reduce
          model parameters and computation cost.
        backward_slice_offset: (optional) int32, default 0, the starting offset to
          slice the feature for backward processing.
        reuse: (optional) Python boolean describing whether to reuse variables
          in an existing scope.  If not `True`, and the existing scope already has
          the given variables, an error is raised.
      """
      super(BidirectionalGridLSTMCell, self).__init__(
          num_units, use_peepholes, share_time_frequency_weights, cell_clip,
          initializer, num_unit_shards, forget_bias, feature_size, frequency_skip,
          num_frequency_blocks, start_freqindex_list, end_freqindex_list,
          couple_input_forget_gates, True, reuse)
      self._backward_slice_offset = int(backward_slice_offset)
      state_names = ""
      for direction in ["fwd", "bwd"]:
        for block_index in range(len(self._num_frequency_blocks)):
          for freq_index in range(self._num_frequency_blocks[block_index]):
            name_prefix = "%s_state_f%02d_b%02d" % (direction, freq_index,
                                                    block_index)
            state_names += ("%s_c, %s_m," % (name_prefix, name_prefix))
      self._state_tuple_type = collections.namedtuple(
          "BidirectionalGridLSTMStateTuple", state_names.strip(","))
      self._state_size = self._state_tuple_type(*(
          [num_units, num_units] * self._total_blocks * 2))
      self._output_size = 2 * num_units * self._total_blocks * 2
  
    def call(self, inputs, state):
      """Run one step of LSTM.
  
      Args:
        inputs: input Tensor, 2D, [batch, num_units].
        state: tuple of Tensors, 2D, [batch, state_size].
  
      Returns:
        A tuple containing:
        - A 2D, [batch, output_dim], Tensor representing the output of the LSTM
          after reading "inputs" when previous state was "state".
          Here output_dim is num_units.
        - A 2D, [batch, state_size], Tensor representing the new state of LSTM
          after reading "inputs" when previous state was "state".
      Raises:
        ValueError: if an input_size was specified and the provided inputs have
          a different dimension.
      """
      batch_size = inputs.shape[0].value or array_ops.shape(inputs)[0]
      fwd_inputs = self._make_tf_features(inputs)
      if self._backward_slice_offset:
        bwd_inputs = self._make_tf_features(inputs, self._backward_slice_offset)
      else:
        bwd_inputs = fwd_inputs
  
      # Forward processing
      with vs.variable_scope("fwd"):
        fwd_m_out_lst = []
        fwd_state_out_lst = []
        for block in range(len(fwd_inputs)):
          fwd_m_out_lst_current, fwd_state_out_lst_current = self._compute(
              fwd_inputs[block],
              block,
              state,
              batch_size,
              state_prefix="fwd_state",
              state_is_tuple=True)
          fwd_m_out_lst.extend(fwd_m_out_lst_current)
          fwd_state_out_lst.extend(fwd_state_out_lst_current)
      # Backward processing
      bwd_m_out_lst = []
      bwd_state_out_lst = []
      with vs.variable_scope("bwd"):
        for block in range(len(bwd_inputs)):
          # Reverse the blocks
          bwd_inputs_reverse = bwd_inputs[block][::-1]
          bwd_m_out_lst_current, bwd_state_out_lst_current = self._compute(
              bwd_inputs_reverse,
              block,
              state,
              batch_size,
              state_prefix="bwd_state",
              state_is_tuple=True)
          bwd_m_out_lst.extend(bwd_m_out_lst_current)
          bwd_state_out_lst.extend(bwd_state_out_lst_current)
      state_out = self._state_tuple_type(*(fwd_state_out_lst + bwd_state_out_lst))
      # Outputs are always concated as it is never used separately.
      m_out = array_ops.concat(fwd_m_out_lst + bwd_m_out_lst, 1)
      return m_out, state_out
  
  
  # pylint: disable=protected-access
  _Linear = core_rnn_cell._Linear  # pylint: disable=invalid-name
  
  # pylint: enable=protected-access
  
  
  class AttentionCellWrapper(rnn_cell_impl.RNNCell):
    """Basic attention cell wrapper.
  
    Implementation based on https://arxiv.org/abs/1409.0473.
    """
  
    def __init__(self,
                 cell,
                 attn_length,
                 attn_size=None,
                 attn_vec_size=None,
                 input_size=None,
                 state_is_tuple=True,
                 reuse=None):
      """Create a cell with attention.
  
      Args:
        cell: an RNNCell, an attention is added to it.
        attn_length: integer, the size of an attention window.
        attn_size: integer, the size of an attention vector. Equal to
            cell.output_size by default.
        attn_vec_size: integer, the number of convolutional features calculated
            on attention state and a size of the hidden layer built from
            base cell state. Equal attn_size to by default.
        input_size: integer, the size of a hidden linear layer,
            built from inputs and attention. Derived from the input tensor
            by default.
        state_is_tuple: If True, accepted and returned states are n-tuples, where
          `n = len(cells)`.  By default (False), the states are all
          concatenated along the column axis.
        reuse: (optional) Python boolean describing whether to reuse variables
          in an existing scope.  If not `True`, and the existing scope already has
          the given variables, an error is raised.
  
      Raises:
        TypeError: if cell is not an RNNCell.
        ValueError: if cell returns a state tuple but the flag
            `state_is_tuple` is `False` or if attn_length is zero or less.
      """
      super(AttentionCellWrapper, self).__init__(_reuse=reuse)
      rnn_cell_impl.assert_like_rnncell("cell", cell)
      if nest.is_sequence(cell.state_size) and not state_is_tuple:
        raise ValueError(
            "Cell returns tuple of states, but the flag "
            "state_is_tuple is not set. State size is: %s" % str(cell.state_size))
      if attn_length <= 0:
        raise ValueError(
            "attn_length should be greater than zero, got %s" % str(attn_length))
      if not state_is_tuple:
        logging.warn("%s: Using a concatenated state is slower and will soon be "
                     "deprecated.  Use state_is_tuple=True.", self)
      if attn_size is None:
        attn_size = cell.output_size
      if attn_vec_size is None:
        attn_vec_size = attn_size
      self._state_is_tuple = state_is_tuple
      self._cell = cell
      self._attn_vec_size = attn_vec_size
      self._input_size = input_size
      self._attn_size = attn_size
      self._attn_length = attn_length
      self._reuse = reuse
      self._linear1 = None
      self._linear2 = None
      self._linear3 = None
  
    @property
    def state_size(self):
      size = (self._cell.state_size, self._attn_size,
              self._attn_size * self._attn_length)
      if self._state_is_tuple:
        return size
      else:
        return sum(list(size))
  
    @property
    def output_size(self):
      return self._attn_size
  
    def call(self, inputs, state):
      """Long short-term memory cell with attention (LSTMA)."""
      if self._state_is_tuple:
        state, attns, attn_states = state
      else:
        states = state
        state = array_ops.slice(states, [0, 0], [-1, self._cell.state_size])
        attns = array_ops.slice(states, [0, self._cell.state_size],
                                [-1, self._attn_size])
        attn_states = array_ops.slice(
            states, [0, self._cell.state_size + self._attn_size],
            [-1, self._attn_size * self._attn_length])
      attn_states = array_ops.reshape(attn_states,
                                      [-1, self._attn_length, self._attn_size])
      input_size = self._input_size
      if input_size is None:
        input_size = inputs.get_shape().as_list()[1]
      if self._linear1 is None:
        self._linear1 = _Linear([inputs, attns], input_size, True)
      inputs = self._linear1([inputs, attns])
      cell_output, new_state = self._cell(inputs, state)
      if self._state_is_tuple:
        new_state_cat = array_ops.concat(nest.flatten(new_state), 1)
      else:
        new_state_cat = new_state
      new_attns, new_attn_states = self._attention(new_state_cat, attn_states)
      with vs.variable_scope("attn_output_projection"):
        if self._linear2 is None:
          self._linear2 = _Linear([cell_output, new_attns], self._attn_size, True)
        output = self._linear2([cell_output, new_attns])
      new_attn_states = array_ops.concat(
          [new_attn_states, array_ops.expand_dims(output, 1)], 1)
      new_attn_states = array_ops.reshape(
          new_attn_states, [-1, self._attn_length * self._attn_size])
      new_state = (new_state, new_attns, new_attn_states)
      if not self._state_is_tuple:
        new_state = array_ops.concat(list(new_state), 1)
      return output, new_state
  
    def _attention(self, query, attn_states):
      conv2d = nn_ops.conv2d
      reduce_sum = math_ops.reduce_sum
      softmax = nn_ops.softmax
      tanh = math_ops.tanh
  
      with vs.variable_scope("attention"):
        k = vs.get_variable("attn_w",
                            [1, 1, self._attn_size, self._attn_vec_size])
        v = vs.get_variable("attn_v", [self._attn_vec_size])
        hidden = array_ops.reshape(attn_states,
                                   [-1, self._attn_length, 1, self._attn_size])
        hidden_features = conv2d(hidden, k, [1, 1, 1, 1], "SAME")
        if self._linear3 is None:
          self._linear3 = _Linear(query, self._attn_vec_size, True)
        y = self._linear3(query)
        y = array_ops.reshape(y, [-1, 1, 1, self._attn_vec_size])
        s = reduce_sum(v * tanh(hidden_features + y), [2, 3])
        a = softmax(s)
        d = reduce_sum(
            array_ops.reshape(a, [-1, self._attn_length, 1, 1]) * hidden, [1, 2])
        new_attns = array_ops.reshape(d, [-1, self._attn_size])
        new_attn_states = array_ops.slice(attn_states, [0, 1, 0], [-1, -1, -1])
        return new_attns, new_attn_states
  
  
  class HighwayWrapper(rnn_cell_impl.RNNCell):
    """RNNCell wrapper that adds highway connection on cell input and output.
  
    Based on:
      R. K. Srivastava, K. Greff, and J. Schmidhuber, "Highway networks",
      arXiv preprint arXiv:1505.00387, 2015.
      https://arxiv.org/abs/1505.00387
    """
  
    def __init__(self,
                 cell,
                 couple_carry_transform_gates=True,
                 carry_bias_init=1.0):
      """Constructs a `HighwayWrapper` for `cell`.
  
      Args:
        cell: An instance of `RNNCell`.
        couple_carry_transform_gates: boolean, should the Carry and Transform gate
          be coupled.
        carry_bias_init: float, carry gates bias initialization.
      """
      self._cell = cell
      self._couple_carry_transform_gates = couple_carry_transform_gates
      self._carry_bias_init = carry_bias_init
  
    @property
    def state_size(self):
      return self._cell.state_size
  
    @property
    def output_size(self):
      return self._cell.output_size
  
    def zero_state(self, batch_size, dtype):
      with ops.name_scope(type(self).__name__ + "ZeroState", values=[batch_size]):
        return self._cell.zero_state(batch_size, dtype)
  
    def _highway(self, inp, out):
      input_size = inp.get_shape().with_rank(2)[1].value
      carry_weight = vs.get_variable("carry_w", [input_size, input_size])
      carry_bias = vs.get_variable(
          "carry_b", [input_size],
          initializer=init_ops.constant_initializer(self._carry_bias_init))
      carry = math_ops.sigmoid(nn_ops.xw_plus_b(inp, carry_weight, carry_bias))
      if self._couple_carry_transform_gates:
        transform = 1 - carry
      else:
        transform_weight = vs.get_variable("transform_w",
                                           [input_size, input_size])
        transform_bias = vs.get_variable(
            "transform_b", [input_size],
            initializer=init_ops.constant_initializer(-self._carry_bias_init))
        transform = math_ops.sigmoid(
            nn_ops.xw_plus_b(inp, transform_weight, transform_bias))
      return inp * carry + out * transform
  
    def __call__(self, inputs, state, scope=None):
      """Run the cell and add its inputs to its outputs.
  
      Args:
        inputs: cell inputs.
        state: cell state.
        scope: optional cell scope.
  
      Returns:
        Tuple of cell outputs and new state.
  
      Raises:
        TypeError: If cell inputs and outputs have different structure (type).
        ValueError: If cell inputs and outputs have different structure (value).
      """
      outputs, new_state = self._cell(inputs, state, scope=scope)
      nest.assert_same_structure(inputs, outputs)
  
      # Ensure shapes match
      def assert_shape_match(inp, out):
        inp.get_shape().assert_is_compatible_with(out.get_shape())
  
      nest.map_structure(assert_shape_match, inputs, outputs)
      res_outputs = nest.map_structure(self._highway, inputs, outputs)
      return (res_outputs, new_state)
  
  
  class LayerNormBasicLSTMCell(rnn_cell_impl.RNNCell):
    """LSTM unit with layer normalization and recurrent dropout.
  
    This class adds layer normalization and recurrent dropout to a
    basic LSTM unit. Layer normalization implementation is based on:
  
      https://arxiv.org/abs/1607.06450.
  
    "Layer Normalization"
    Jimmy Lei Ba, Jamie Ryan Kiros, Geoffrey E. Hinton
  
    and is applied before the internal nonlinearities.
    Recurrent dropout is base on:
  
      https://arxiv.org/abs/1603.05118
  
    "Recurrent Dropout without Memory Loss"
    Stanislau Semeniuta, Aliaksei Severyn, Erhardt Barth.
    """
  
    def __init__(self,
                 num_units,
                 forget_bias=1.0,
                 input_size=None,
                 activation=math_ops.tanh,
                 layer_norm=True,
                 norm_gain=1.0,
                 norm_shift=0.0,
                 dropout_keep_prob=1.0,
                 dropout_prob_seed=None,
                 reuse=None):
      """Initializes the basic LSTM cell.
  
      Args:
        num_units: int, The number of units in the LSTM cell.
        forget_bias: float, The bias added to forget gates (see above).
        input_size: Deprecated and unused.
        activation: Activation function of the inner states.
        layer_norm: If `True`, layer normalization will be applied.
        norm_gain: float, The layer normalization gain initial value. If
          `layer_norm` has been set to `False`, this argument will be ignored.
        norm_shift: float, The layer normalization shift initial value. If
          `layer_norm` has been set to `False`, this argument will be ignored.
        dropout_keep_prob: unit Tensor or float between 0 and 1 representing the
          recurrent dropout probability value. If float and 1.0, no dropout will
          be applied.
        dropout_prob_seed: (optional) integer, the randomness seed.
        reuse: (optional) Python boolean describing whether to reuse variables
          in an existing scope.  If not `True`, and the existing scope already has
          the given variables, an error is raised.
      """
      super(LayerNormBasicLSTMCell, self).__init__(_reuse=reuse)
  
      if input_size is not None:
        logging.warn("%s: The input_size parameter is deprecated.", self)
  
      self._num_units = num_units
      self._activation = activation
      self._forget_bias = forget_bias
      self._keep_prob = dropout_keep_prob
      self._seed = dropout_prob_seed
      self._layer_norm = layer_norm
      self._norm_gain = norm_gain
      self._norm_shift = norm_shift
      self._reuse = reuse
  
    @property
    def state_size(self):
      return rnn_cell_impl.LSTMStateTuple(self._num_units, self._num_units)
  
    @property
    def output_size(self):
      return self._num_units
  
    def _norm(self, inp, scope, dtype=dtypes.float32):
      shape = inp.get_shape()[-1:]
      gamma_init = init_ops.constant_initializer(self._norm_gain)
      beta_init = init_ops.constant_initializer(self._norm_shift)
      with vs.variable_scope(scope):
        # Initialize beta and gamma for use by layer_norm.
        vs.get_variable("gamma", shape=shape, initializer=gamma_init, dtype=dtype)
        vs.get_variable("beta", shape=shape, initializer=beta_init, dtype=dtype)
      normalized = layers.layer_norm(inp, reuse=True, scope=scope)
      return normalized
  
    def _linear(self, args):
      out_size = 4 * self._num_units
      proj_size = args.get_shape()[-1]
      dtype = args.dtype
      weights = vs.get_variable("kernel", [proj_size, out_size], dtype=dtype)
      out = math_ops.matmul(args, weights)
      if not self._layer_norm:
        bias = vs.get_variable("bias", [out_size], dtype=dtype)
        out = nn_ops.bias_add(out, bias)
      return out
  
    def call(self, inputs, state):
      """LSTM cell with layer normalization and recurrent dropout."""
      c, h = state
      args = array_ops.concat([inputs, h], 1)
      concat = self._linear(args)
      dtype = args.dtype
  
      i, j, f, o = array_ops.split(value=concat, num_or_size_splits=4, axis=1)
      if self._layer_norm:
        i = self._norm(i, "input", dtype=dtype)
        j = self._norm(j, "transform", dtype=dtype)
        f = self._norm(f, "forget", dtype=dtype)
        o = self._norm(o, "output", dtype=dtype)
  
      g = self._activation(j)
      if (not isinstance(self._keep_prob, float)) or self._keep_prob < 1:
        g = nn_ops.dropout(g, self._keep_prob, seed=self._seed)
  
      new_c = (
          c * math_ops.sigmoid(f + self._forget_bias) + math_ops.sigmoid(i) * g)
      if self._layer_norm:
        new_c = self._norm(new_c, "state", dtype=dtype)
      new_h = self._activation(new_c) * math_ops.sigmoid(o)
  
      new_state = rnn_cell_impl.LSTMStateTuple(new_c, new_h)
      return new_h, new_state
  
  
  class NASCell(rnn_cell_impl.RNNCell):
    """Neural Architecture Search (NAS) recurrent network cell.
  
    This implements the recurrent cell from the paper:
  
      https://arxiv.org/abs/1611.01578
  
    Barret Zoph and Quoc V. Le.
    "Neural Architecture Search with Reinforcement Learning" Proc. ICLR 2017.
  
    The class uses an optional projection layer.
    """
  
    def __init__(self, num_units, num_proj=None, use_biases=False, reuse=None):
      """Initialize the parameters for a NAS cell.
  
      Args:
        num_units: int, The number of units in the NAS cell
        num_proj: (optional) int, The output dimensionality for the projection
          matrices.  If None, no projection is performed.
        use_biases: (optional) bool, If True then use biases within the cell. This
          is False by default.
        reuse: (optional) Python boolean describing whether to reuse variables
          in an existing scope.  If not `True`, and the existing scope already has
          the given variables, an error is raised.
      """
      super(NASCell, self).__init__(_reuse=reuse)
      self._num_units = num_units
      self._num_proj = num_proj
      self._use_biases = use_biases
      self._reuse = reuse
  
      if num_proj is not None:
        self._state_size = rnn_cell_impl.LSTMStateTuple(num_units, num_proj)
        self._output_size = num_proj
      else:
        self._state_size = rnn_cell_impl.LSTMStateTuple(num_units, num_units)
        self._output_size = num_units
  
    @property
    def state_size(self):
      return self._state_size
  
    @property
    def output_size(self):
      return self._output_size
  
    def call(self, inputs, state):
      """Run one step of NAS Cell.
  
      Args:
        inputs: input Tensor, 2D, batch x num_units.
        state: This must be a tuple of state Tensors, both `2-D`, with column
          sizes `c_state` and `m_state`.
  
      Returns:
        A tuple containing:
        - A `2-D, [batch x output_dim]`, Tensor representing the output of the
          NAS Cell after reading `inputs` when previous state was `state`.
          Here output_dim is:
             num_proj if num_proj was set,
             num_units otherwise.
        - Tensor(s) representing the new state of NAS Cell after reading `inputs`
          when the previous state was `state`.  Same type and shape(s) as `state`.
  
      Raises:
        ValueError: If input size cannot be inferred from inputs via
          static shape inference.
      """
      sigmoid = math_ops.sigmoid
      tanh = math_ops.tanh
      relu = nn_ops.relu
  
      num_proj = self._num_units if self._num_proj is None else self._num_proj
  
      (c_prev, m_prev) = state
  
      dtype = inputs.dtype
      input_size = inputs.get_shape().with_rank(2)[1]
      if input_size.value is None:
        raise ValueError("Could not infer input size from inputs.get_shape()[-1]")
      # Variables for the NAS cell. W_m is all matrices multiplying the
      # hiddenstate and W_inputs is all matrices multiplying the inputs.
      concat_w_m = vs.get_variable("recurrent_kernel",
                                   [num_proj, 8 * self._num_units], dtype)
      concat_w_inputs = vs.get_variable(
          "kernel", [input_size.value, 8 * self._num_units], dtype)
  
      m_matrix = math_ops.matmul(m_prev, concat_w_m)
      inputs_matrix = math_ops.matmul(inputs, concat_w_inputs)
  
      if self._use_biases:
        b = vs.get_variable(
            "bias",
            shape=[8 * self._num_units],
            initializer=init_ops.zeros_initializer(),
            dtype=dtype)
        m_matrix = nn_ops.bias_add(m_matrix, b)
  
      # The NAS cell branches into 8 different splits for both the hiddenstate
      # and the input
      m_matrix_splits = array_ops.split(
          axis=1, num_or_size_splits=8, value=m_matrix)
      inputs_matrix_splits = array_ops.split(
          axis=1, num_or_size_splits=8, value=inputs_matrix)
  
      # First layer
      layer1_0 = sigmoid(inputs_matrix_splits[0] + m_matrix_splits[0])
      layer1_1 = relu(inputs_matrix_splits[1] + m_matrix_splits[1])
      layer1_2 = sigmoid(inputs_matrix_splits[2] + m_matrix_splits[2])
      layer1_3 = relu(inputs_matrix_splits[3] * m_matrix_splits[3])
      layer1_4 = tanh(inputs_matrix_splits[4] + m_matrix_splits[4])
      layer1_5 = sigmoid(inputs_matrix_splits[5] + m_matrix_splits[5])
      layer1_6 = tanh(inputs_matrix_splits[6] + m_matrix_splits[6])
      layer1_7 = sigmoid(inputs_matrix_splits[7] + m_matrix_splits[7])
  
      # Second layer
      l2_0 = tanh(layer1_0 * layer1_1)
      l2_1 = tanh(layer1_2 + layer1_3)
      l2_2 = tanh(layer1_4 * layer1_5)
      l2_3 = sigmoid(layer1_6 + layer1_7)
  
      # Inject the cell
      l2_0 = tanh(l2_0 + c_prev)
  
      # Third layer
      l3_0_pre = l2_0 * l2_1
      new_c = l3_0_pre  # create new cell
      l3_0 = l3_0_pre
      l3_1 = tanh(l2_2 + l2_3)
  
      # Final layer
      new_m = tanh(l3_0 * l3_1)
  
      # Projection layer if specified
      if self._num_proj is not None:
        concat_w_proj = vs.get_variable("projection_weights",
                                        [self._num_units, self._num_proj], dtype)
        new_m = math_ops.matmul(new_m, concat_w_proj)
  
      new_state = rnn_cell_impl.LSTMStateTuple(new_c, new_m)
      return new_m, new_state
  
  
  class UGRNNCell(rnn_cell_impl.RNNCell):
    """Update Gate Recurrent Neural Network (UGRNN) cell.
  
    Compromise between a LSTM/GRU and a vanilla RNN.  There is only one
    gate, and that is to determine whether the unit should be
    integrating or computing instantaneously.  This is the recurrent
    idea of the feedforward Highway Network.
  
    This implements the recurrent cell from the paper:
  
      https://arxiv.org/abs/1611.09913
  
    Jasmine Collins, Jascha Sohl-Dickstein, and David Sussillo.
    "Capacity and Trainability in Recurrent Neural Networks" Proc. ICLR 2017.
    """
  
    def __init__(self,
                 num_units,
                 initializer=None,
                 forget_bias=1.0,
                 activation=math_ops.tanh,
                 reuse=None):
      """Initialize the parameters for an UGRNN cell.
  
      Args:
        num_units: int, The number of units in the UGRNN cell
        initializer: (optional) The initializer to use for the weight matrices.
        forget_bias: (optional) float, default 1.0, The initial bias of the
          forget gate, used to reduce the scale of forgetting at the beginning
          of the training.
        activation: (optional) Activation function of the inner states.
          Default is `tf.tanh`.
        reuse: (optional) Python boolean describing whether to reuse variables
          in an existing scope.  If not `True`, and the existing scope already has
          the given variables, an error is raised.
      """
      super(UGRNNCell, self).__init__(_reuse=reuse)
      self._num_units = num_units
      self._initializer = initializer
      self._forget_bias = forget_bias
      self._activation = activation
      self._reuse = reuse
      self._linear = None
  
    @property
    def state_size(self):
      return self._num_units
  
    @property
    def output_size(self):
      return self._num_units
  
    def call(self, inputs, state):
      """Run one step of UGRNN.
  
      Args:
        inputs: input Tensor, 2D, batch x input size.
        state: state Tensor, 2D, batch x num units.
  
      Returns:
        new_output: batch x num units, Tensor representing the output of the UGRNN
          after reading `inputs` when previous state was `state`. Identical to
          `new_state`.
        new_state: batch x num units, Tensor representing the state of the UGRNN
          after reading `inputs` when previous state was `state`.
  
      Raises:
        ValueError: If input size cannot be inferred from inputs via
          static shape inference.
      """
      sigmoid = math_ops.sigmoid
  
      input_size = inputs.get_shape().with_rank(2)[1]
      if input_size.value is None:
        raise ValueError("Could not infer input size from inputs.get_shape()[-1]")
  
      with vs.variable_scope(
          vs.get_variable_scope(), initializer=self._initializer):
        cell_inputs = array_ops.concat([inputs, state], 1)
        if self._linear is None:
          self._linear = _Linear(cell_inputs, 2 * self._num_units, True)
        rnn_matrix = self._linear(cell_inputs)
  
        [g_act, c_act] = array_ops.split(
            axis=1, num_or_size_splits=2, value=rnn_matrix)
  
        c = self._activation(c_act)
        g = sigmoid(g_act + self._forget_bias)
        new_state = g * state + (1.0 - g) * c
        new_output = new_state
  
      return new_output, new_state
  
  
  class IntersectionRNNCell(rnn_cell_impl.RNNCell):
    """Intersection Recurrent Neural Network (+RNN) cell.
  
    Architecture with coupled recurrent gate as well as coupled depth
    gate, designed to improve information flow through stacked RNNs. As the
    architecture uses depth gating, the dimensionality of the depth
    output (y) also should not change through depth (input size == output size).
    To achieve this, the first layer of a stacked Intersection RNN projects
    the inputs to N (num units) dimensions. Therefore when initializing an
    IntersectionRNNCell, one should set `num_in_proj = N` for the first layer
    and use default settings for subsequent layers.
  
    This implements the recurrent cell from the paper:
  
      https://arxiv.org/abs/1611.09913
  
    Jasmine Collins, Jascha Sohl-Dickstein, and David Sussillo.
    "Capacity and Trainability in Recurrent Neural Networks" Proc. ICLR 2017.
  
    The Intersection RNN is built for use in deeply stacked
    RNNs so it may not achieve best performance with depth 1.
    """
  
    def __init__(self,
                 num_units,
                 num_in_proj=None,
                 initializer=None,
                 forget_bias=1.0,
                 y_activation=nn_ops.relu,
                 reuse=None):
      """Initialize the parameters for an +RNN cell.
  
      Args:
        num_units: int, The number of units in the +RNN cell
        num_in_proj: (optional) int, The input dimensionality for the RNN.
          If creating the first layer of an +RNN, this should be set to
          `num_units`. Otherwise, this should be set to `None` (default).
          If `None`, dimensionality of `inputs` should be equal to `num_units`,
          otherwise ValueError is thrown.
        initializer: (optional) The initializer to use for the weight matrices.
        forget_bias: (optional) float, default 1.0, The initial bias of the
          forget gates, used to reduce the scale of forgetting at the beginning
          of the training.
        y_activation: (optional) Activation function of the states passed
          through depth. Default is 'tf.nn.relu`.
        reuse: (optional) Python boolean describing whether to reuse variables
          in an existing scope.  If not `True`, and the existing scope already has
          the given variables, an error is raised.
      """
      super(IntersectionRNNCell, self).__init__(_reuse=reuse)
      self._num_units = num_units
      self._initializer = initializer
      self._forget_bias = forget_bias
      self._num_input_proj = num_in_proj
      self._y_activation = y_activation
      self._reuse = reuse
      self._linear1 = None
      self._linear2 = None
  
    @property
    def state_size(self):
      return self._num_units
  
    @property
    def output_size(self):
      return self._num_units
  
    def call(self, inputs, state):
      """Run one step of the Intersection RNN.
  
      Args:
        inputs: input Tensor, 2D, batch x input size.
        state: state Tensor, 2D, batch x num units.
  
      Returns:
        new_y: batch x num units, Tensor representing the output of the +RNN
          after reading `inputs` when previous state was `state`.
        new_state: batch x num units, Tensor representing the state of the +RNN
          after reading `inputs` when previous state was `state`.
  
      Raises:
        ValueError: If input size cannot be inferred from `inputs` via
          static shape inference.
        ValueError: If input size != output size (these must be equal when
          using the Intersection RNN).
      """
      sigmoid = math_ops.sigmoid
      tanh = math_ops.tanh
  
      input_size = inputs.get_shape().with_rank(2)[1]
      if input_size.value is None:
        raise ValueError("Could not infer input size from inputs.get_shape()[-1]")
  
      with vs.variable_scope(
          vs.get_variable_scope(), initializer=self._initializer):
        # read-in projections (should be used for first layer in deep +RNN
        # to transform size of inputs from I --> N)
        if input_size.value != self._num_units:
          if self._num_input_proj:
            with vs.variable_scope("in_projection"):
              if self._linear1 is None:
                self._linear1 = _Linear(inputs, self._num_units, True)
              inputs = self._linear1(inputs)
          else:
            raise ValueError("Must have input size == output size for "
                             "Intersection RNN. To fix, num_in_proj should "
                             "be set to num_units at cell init.")
  
        n_dim = i_dim = self._num_units
        cell_inputs = array_ops.concat([inputs, state], 1)
        if self._linear2 is None:
          self._linear2 = _Linear(cell_inputs, 2 * n_dim + 2 * i_dim, True)
        rnn_matrix = self._linear2(cell_inputs)
  
        gh_act = rnn_matrix[:, :n_dim]  # b x n
        h_act = rnn_matrix[:, n_dim:2 * n_dim]  # b x n
        gy_act = rnn_matrix[:, 2 * n_dim:2 * n_dim + i_dim]  # b x i
        y_act = rnn_matrix[:, 2 * n_dim + i_dim:2 * n_dim + 2 * i_dim]  # b x i
  
        h = tanh(h_act)
        y = self._y_activation(y_act)
        gh = sigmoid(gh_act + self._forget_bias)
        gy = sigmoid(gy_act + self._forget_bias)
  
        new_state = gh * state + (1.0 - gh) * h  # passed thru time
        new_y = gy * inputs + (1.0 - gy) * y  # passed thru depth
  
      return new_y, new_state
  
  
  _REGISTERED_OPS = None
  
  
  class CompiledWrapper(rnn_cell_impl.RNNCell):
    """Wraps step execution in an XLA JIT scope."""
  
    def __init__(self, cell, compile_stateful=False):
      """Create CompiledWrapper cell.
  
      Args:
        cell: Instance of `RNNCell`.
        compile_stateful: Whether to compile stateful ops like initializers
          and random number generators (default: False).
      """
      self._cell = cell
      self._compile_stateful = compile_stateful
  
    @property
    def state_size(self):
      return self._cell.state_size
  
    @property
    def output_size(self):
      return self._cell.output_size
  
    def zero_state(self, batch_size, dtype):
      with ops.name_scope(type(self).__name__ + "ZeroState", values=[batch_size]):
        return self._cell.zero_state(batch_size, dtype)
  
    def __call__(self, inputs, state, scope=None):
      if self._compile_stateful:
        compile_ops = True
      else:
  
        def compile_ops(node_def):
          global _REGISTERED_OPS
          if _REGISTERED_OPS is None:
            _REGISTERED_OPS = op_def_registry.get_registered_ops()
          return not _REGISTERED_OPS[node_def.op].is_stateful
  
      with jit.experimental_jit_scope(compile_ops=compile_ops):
        return self._cell(inputs, state, scope=scope)
  
  
  def _random_exp_initializer(minval, maxval, seed=None, dtype=dtypes.float32):
    """Returns an exponential distribution initializer.
  
    Args:
      minval: float or a scalar float Tensor. With value > 0. Lower bound of the
          range of random values to generate.
      maxval: float or a scalar float Tensor. With value > minval. Upper bound of
          the range of random values to generate.
      seed: An integer. Used to create random seeds.
      dtype: The data type.
  
    Returns:
      An initializer that generates tensors with an exponential distribution.
    """
  
    def _initializer(shape, dtype=dtype, partition_info=None):
      del partition_info  # Unused.
      return math_ops.exp(
          random_ops.random_uniform(
              shape, math_ops.log(minval), math_ops.log(maxval), dtype,
              seed=seed))
  
    return _initializer
  
  
  class PhasedLSTMCell(rnn_cell_impl.RNNCell):
    """Phased LSTM recurrent network cell.
  
    https://arxiv.org/pdf/1610.09513v1.pdf
    """
  
    def __init__(self,
                 num_units,
                 use_peepholes=False,
                 leak=0.001,
                 ratio_on=0.1,
                 trainable_ratio_on=True,
                 period_init_min=1.0,
                 period_init_max=1000.0,
                 reuse=None):
      """Initialize the Phased LSTM cell.
  
      Args:
        num_units: int, The number of units in the Phased LSTM cell.
        use_peepholes: bool, set True to enable peephole connections.
        leak: float or scalar float Tensor with value in [0, 1]. Leak applied
            during training.
        ratio_on: float or scalar float Tensor with value in [0, 1]. Ratio of the
            period during which the gates are open.
        trainable_ratio_on: bool, weather ratio_on is trainable.
        period_init_min: float or scalar float Tensor. With value > 0.
            Minimum value of the initialized period.
            The period values are initialized by drawing from the distribution:
            e^U(log(period_init_min), log(period_init_max))
            Where U(.,.) is the uniform distribution.
        period_init_max: float or scalar float Tensor.
            With value > period_init_min. Maximum value of the initialized period.
        reuse: (optional) Python boolean describing whether to reuse variables
          in an existing scope. If not `True`, and the existing scope already has
          the given variables, an error is raised.
      """
      super(PhasedLSTMCell, self).__init__(_reuse=reuse)
      self._num_units = num_units
      self._use_peepholes = use_peepholes
      self._leak = leak
      self._ratio_on = ratio_on
      self._trainable_ratio_on = trainable_ratio_on
      self._period_init_min = period_init_min
      self._period_init_max = period_init_max
      self._reuse = reuse
      self._linear1 = None
      self._linear2 = None
      self._linear3 = None
  
    @property
    def state_size(self):
      return rnn_cell_impl.LSTMStateTuple(self._num_units, self._num_units)
  
    @property
    def output_size(self):
      return self._num_units
  
    def _mod(self, x, y):
      """Modulo function that propagates x gradients."""
      return array_ops.stop_gradient(math_ops.mod(x, y) - x) + x
  
    def _get_cycle_ratio(self, time, phase, period):
      """Compute the cycle ratio in the dtype of the time."""
      phase_casted = math_ops.cast(phase, dtype=time.dtype)
      period_casted = math_ops.cast(period, dtype=time.dtype)
      shifted_time = time - phase_casted
      cycle_ratio = self._mod(shifted_time, period_casted) / period_casted
      return math_ops.cast(cycle_ratio, dtype=dtypes.float32)
  
    def call(self, inputs, state):
      """Phased LSTM Cell.
  
      Args:
        inputs: A tuple of 2 Tensor.
           The first Tensor has shape [batch, 1], and type float32 or float64.
           It stores the time.
           The second Tensor has shape [batch, features_size], and type float32.
           It stores the features.
        state: rnn_cell_impl.LSTMStateTuple, state from previous timestep.
  
      Returns:
        A tuple containing:
        - A Tensor of float32, and shape [batch_size, num_units], representing the
          output of the cell.
        - A rnn_cell_impl.LSTMStateTuple, containing 2 Tensors of float32, shape
          [batch_size, num_units], representing the new state and the output.
      """
      (c_prev, h_prev) = state
      (time, x) = inputs
  
      in_mask_gates = [x, h_prev]
      if self._use_peepholes:
        in_mask_gates.append(c_prev)
  
      with vs.variable_scope("mask_gates"):
        if self._linear1 is None:
          self._linear1 = _Linear(in_mask_gates, 2 * self._num_units, True)
  
        mask_gates = math_ops.sigmoid(self._linear1(in_mask_gates))
        [input_gate, forget_gate] = array_ops.split(
            axis=1, num_or_size_splits=2, value=mask_gates)
  
      with vs.variable_scope("new_input"):
        if self._linear2 is None:
          self._linear2 = _Linear([x, h_prev], self._num_units, True)
        new_input = math_ops.tanh(self._linear2([x, h_prev]))
  
      new_c = (c_prev * forget_gate + input_gate * new_input)
  
      in_out_gate = [x, h_prev]
      if self._use_peepholes:
        in_out_gate.append(new_c)
  
      with vs.variable_scope("output_gate"):
        if self._linear3 is None:
          self._linear3 = _Linear(in_out_gate, self._num_units, True)
        output_gate = math_ops.sigmoid(self._linear3(in_out_gate))
  
      new_h = math_ops.tanh(new_c) * output_gate
  
      period = vs.get_variable(
          "period", [self._num_units],
          initializer=_random_exp_initializer(self._period_init_min,
                                              self._period_init_max))
      phase = vs.get_variable(
          "phase", [self._num_units],
          initializer=init_ops.random_uniform_initializer(0.,
                                                          period.initial_value))
      ratio_on = vs.get_variable(
          "ratio_on", [self._num_units],
          initializer=init_ops.constant_initializer(self._ratio_on),
          trainable=self._trainable_ratio_on)
  
      cycle_ratio = self._get_cycle_ratio(time, phase, period)
  
      k_up = 2 * cycle_ratio / ratio_on
      k_down = 2 - k_up
      k_closed = self._leak * cycle_ratio
  
      k = array_ops.where(cycle_ratio < ratio_on, k_down, k_closed)
      k = array_ops.where(cycle_ratio < 0.5 * ratio_on, k_up, k)
  
      new_c = k * new_c + (1 - k) * c_prev
      new_h = k * new_h + (1 - k) * h_prev
  
      new_state = rnn_cell_impl.LSTMStateTuple(new_c, new_h)
  
      return new_h, new_state
  
  
  class ConvLSTMCell(rnn_cell_impl.RNNCell):
    """Convolutional LSTM recurrent network cell.
  
    https://arxiv.org/pdf/1506.04214v1.pdf
    """
  
    def __init__(self,
                 conv_ndims,
                 input_shape,
                 output_channels,
                 kernel_shape,
                 use_bias=True,
                 skip_connection=False,
                 forget_bias=1.0,
                 initializers=None,
                 name="conv_lstm_cell"):
      """Construct ConvLSTMCell.
  
      Args:
        conv_ndims: Convolution dimensionality (1, 2 or 3).
        input_shape: Shape of the input as int tuple, excluding the batch size.
        output_channels: int, number of output channels of the conv LSTM.
        kernel_shape: Shape of kernel as in tuple (of size 1,2 or 3).
        use_bias: (bool) Use bias in convolutions.
        skip_connection: If set to `True`, concatenate the input to the
          output of the conv LSTM. Default: `False`.
        forget_bias: Forget bias.
        initializers: Unused.
        name: Name of the module.
  
      Raises:
        ValueError: If `skip_connection` is `True` and stride is different from 1
          or if `input_shape` is incompatible with `conv_ndims`.
      """
      super(ConvLSTMCell, self).__init__(name=name)
  
      if conv_ndims != len(input_shape) - 1:
        raise ValueError("Invalid input_shape {} for conv_ndims={}.".format(
            input_shape, conv_ndims))
  
      self._conv_ndims = conv_ndims
      self._input_shape = input_shape
      self._output_channels = output_channels
      self._kernel_shape = kernel_shape
      self._use_bias = use_bias
      self._forget_bias = forget_bias
      self._skip_connection = skip_connection
  
      self._total_output_channels = output_channels
      if self._skip_connection:
        self._total_output_channels += self._input_shape[-1]
  
      state_size = tensor_shape.TensorShape(
          self._input_shape[:-1] + [self._output_channels])
      self._state_size = rnn_cell_impl.LSTMStateTuple(state_size, state_size)
      self._output_size = tensor_shape.TensorShape(
          self._input_shape[:-1] + [self._total_output_channels])
  
    @property
    def output_size(self):
      return self._output_size
  
    @property
    def state_size(self):
      return self._state_size
  
    def call(self, inputs, state, scope=None):
      cell, hidden = state
      new_hidden = _conv([inputs, hidden], self._kernel_shape,
                         4 * self._output_channels, self._use_bias)
      gates = array_ops.split(
          value=new_hidden, num_or_size_splits=4, axis=self._conv_ndims + 1)
  
      input_gate, new_input, forget_gate, output_gate = gates
      new_cell = math_ops.sigmoid(forget_gate + self._forget_bias) * cell
      new_cell += math_ops.sigmoid(input_gate) * math_ops.tanh(new_input)
      output = math_ops.tanh(new_cell) * math_ops.sigmoid(output_gate)
  
      if self._skip_connection:
        output = array_ops.concat([output, inputs], axis=-1)
      new_state = rnn_cell_impl.LSTMStateTuple(new_cell, output)
      return output, new_state
  
  
  class Conv1DLSTMCell(ConvLSTMCell):
    """1D Convolutional LSTM recurrent network cell.
  
    https://arxiv.org/pdf/1506.04214v1.pdf
    """
  
    def __init__(self, name="conv_1d_lstm_cell", **kwargs):
      """Construct Conv1DLSTM. See `ConvLSTMCell` for more details."""
      super(Conv1DLSTMCell, self).__init__(conv_ndims=1, name=name, **kwargs)
  
  
  class Conv2DLSTMCell(ConvLSTMCell):
    """2D Convolutional LSTM recurrent network cell.
  
    https://arxiv.org/pdf/1506.04214v1.pdf
    """
  
    def __init__(self, name="conv_2d_lstm_cell", **kwargs):
      """Construct Conv2DLSTM. See `ConvLSTMCell` for more details."""
      super(Conv2DLSTMCell, self).__init__(conv_ndims=2, name=name, **kwargs)
  
  
  class Conv3DLSTMCell(ConvLSTMCell):
    """3D Convolutional LSTM recurrent network cell.
  
    https://arxiv.org/pdf/1506.04214v1.pdf
    """
  
    def __init__(self, name="conv_3d_lstm_cell", **kwargs):
      """Construct Conv3DLSTM. See `ConvLSTMCell` for more details."""
      super(Conv3DLSTMCell, self).__init__(conv_ndims=3, name=name, **kwargs)
  
  
  def _conv(args, filter_size, num_features, bias, bias_start=0.0):
    """Convolution.
  
    Args:
      args: a Tensor or a list of Tensors of dimension 3D, 4D or 5D,
      batch x n, Tensors.
      filter_size: int tuple of filter height and width.
      num_features: int, number of features.
      bias: Whether to use biases in the convolution layer.
      bias_start: starting value to initialize the bias; 0 by default.
  
    Returns:
      A 3D, 4D, or 5D Tensor with shape [batch ... num_features]
  
    Raises:
      ValueError: if some of the arguments has unspecified or wrong shape.
    """
  
    # Calculate the total size of arguments on dimension 1.
    total_arg_size_depth = 0
    shapes = [a.get_shape().as_list() for a in args]
    shape_length = len(shapes[0])
    for shape in shapes:
      if len(shape) not in [3, 4, 5]:
        raise ValueError("Conv Linear expects 3D, 4D "
                         "or 5D arguments: %s" % str(shapes))
      if len(shape) != len(shapes[0]):
        raise ValueError("Conv Linear expects all args "
                         "to be of same Dimension: %s" % str(shapes))
      else:
        total_arg_size_depth += shape[-1]
    dtype = [a.dtype for a in args][0]
  
    # determine correct conv operation
    if shape_length == 3:
      conv_op = nn_ops.conv1d
      strides = 1
    elif shape_length == 4:
      conv_op = nn_ops.conv2d
      strides = shape_length * [1]
    elif shape_length == 5:
      conv_op = nn_ops.conv3d
      strides = shape_length * [1]
  
    # Now the computation.
    kernel = vs.get_variable(
        "kernel", filter_size + [total_arg_size_depth, num_features], dtype=dtype)
    if len(args) == 1:
      res = conv_op(args[0], kernel, strides, padding="SAME")
    else:
      res = conv_op(
          array_ops.concat(axis=shape_length - 1, values=args),
          kernel,
          strides,
          padding="SAME")
    if not bias:
      return res
    bias_term = vs.get_variable(
        "biases", [num_features],
        dtype=dtype,
        initializer=init_ops.constant_initializer(bias_start, dtype=dtype))
    return res + bias_term
  
  
  class GLSTMCell(rnn_cell_impl.RNNCell):
    """Group LSTM cell (G-LSTM).
  
    The implementation is based on:
  
      https://arxiv.org/abs/1703.10722
  
    O. Kuchaiev and B. Ginsburg
    "Factorization Tricks for LSTM Networks", ICLR 2017 workshop.
  
    In brief, a G-LSTM cell consists of one LSTM sub-cell per group, where each
    sub-cell operates on an evenly-sized sub-vector of the input and produces an
    evenly-sized sub-vector of the output.  For example, a G-LSTM cell with 128
    units and 4 groups consists of 4 LSTMs sub-cells with 32 units each.  If that
    G-LSTM cell is fed a 200-dim input, then each sub-cell receives a 50-dim part
    of the input and produces a 32-dim part of the output.
    """
  
    def __init__(self,
                 num_units,
                 initializer=None,
                 num_proj=None,
                 number_of_groups=1,
                 forget_bias=1.0,
                 activation=math_ops.tanh,
                 reuse=None):
      """Initialize the parameters of G-LSTM cell.
  
      Args:
        num_units: int, The number of units in the G-LSTM cell
        initializer: (optional) The initializer to use for the weight and
          projection matrices.
        num_proj: (optional) int, The output dimensionality for the projection
          matrices.  If None, no projection is performed.
        number_of_groups: (optional) int, number of groups to use.
          If `number_of_groups` is 1, then it should be equivalent to LSTM cell
        forget_bias: Biases of the forget gate are initialized by default to 1
          in order to reduce the scale of forgetting at the beginning of
          the training.
        activation: Activation function of the inner states.
        reuse: (optional) Python boolean describing whether to reuse variables
          in an existing scope.  If not `True`, and the existing scope already
          has the given variables, an error is raised.
  
      Raises:
        ValueError: If `num_units` or `num_proj` is not divisible by
          `number_of_groups`.
      """
      super(GLSTMCell, self).__init__(_reuse=reuse)
      self._num_units = num_units
      self._initializer = initializer
      self._num_proj = num_proj
      self._forget_bias = forget_bias
      self._activation = activation
      self._number_of_groups = number_of_groups
  
      if self._num_units % self._number_of_groups != 0:
        raise ValueError("num_units must be divisible by number_of_groups")
      if self._num_proj:
        if self._num_proj % self._number_of_groups != 0:
          raise ValueError("num_proj must be divisible by number_of_groups")
        self._group_shape = [
            int(self._num_proj / self._number_of_groups),
            int(self._num_units / self._number_of_groups)
        ]
      else:
        self._group_shape = [
            int(self._num_units / self._number_of_groups),
            int(self._num_units / self._number_of_groups)
        ]
  
      if num_proj:
        self._state_size = rnn_cell_impl.LSTMStateTuple(num_units, num_proj)
        self._output_size = num_proj
      else:
        self._state_size = rnn_cell_impl.LSTMStateTuple(num_units, num_units)
        self._output_size = num_units
      self._linear1 = [None] * number_of_groups
      self._linear2 = None
  
    @property
    def state_size(self):
      return self._state_size
  
    @property
    def output_size(self):
      return self._output_size
  
    def _get_input_for_group(self, inputs, group_id, group_size):
      """Slices inputs into groups to prepare for processing by cell's groups.
  
      Args:
        inputs: cell input or it's previous state,
                a Tensor, 2D, [batch x num_units]
        group_id: group id, a Scalar, for which to prepare input
        group_size: size of the group
  
      Returns:
        subset of inputs corresponding to group "group_id",
        a Tensor, 2D, [batch x num_units/number_of_groups]
      """
      return array_ops.slice(
          input_=inputs,
          begin=[0, group_id * group_size],
          size=[self._batch_size, group_size],
          name=("GLSTM_group%d_input_generation" % group_id))
  
    def call(self, inputs, state):
      """Run one step of G-LSTM.
  
      Args:
        inputs: input Tensor, 2D, [batch x num_inputs].  num_inputs must be
          statically-known and evenly divisible into groups.  The innermost
          vectors of the inputs are split into evenly-sized sub-vectors and fed
          into the per-group LSTM sub-cells.
        state: this must be a tuple of state Tensors, both `2-D`, with column
          sizes `c_state` and `m_state`.
  
      Returns:
        A tuple containing:
  
        - A `2-D, [batch x output_dim]`, Tensor representing the output of the
          G-LSTM after reading `inputs` when previous state was `state`.
          Here output_dim is:
             num_proj if num_proj was set,
             num_units otherwise.
        - LSTMStateTuple representing the new state of G-LSTM cell
          after reading `inputs` when the previous state was `state`.
  
      Raises:
        ValueError: If input size cannot be inferred from inputs via
          static shape inference, or if the input shape is incompatible
          with the number of groups.
      """
      (c_prev, m_prev) = state
  
      self._batch_size = inputs.shape[0].value or array_ops.shape(inputs)[0]
  
      # If the input size is statically-known, calculate and validate its group
      # size.  Otherwise, use the output group size.
      input_size = inputs.shape[1].value
      if input_size is None:
        raise ValueError("input size must be statically known")
      if input_size % self._number_of_groups != 0:
        raise ValueError(
            "input size (%d) must be divisible by number_of_groups (%d)" %
            (input_size, self._number_of_groups))
      input_group_size = int(input_size / self._number_of_groups)
  
      dtype = inputs.dtype
      scope = vs.get_variable_scope()
      with vs.variable_scope(scope, initializer=self._initializer):
        i_parts = []
        j_parts = []
        f_parts = []
        o_parts = []
  
        for group_id in range(self._number_of_groups):
          with vs.variable_scope("group%d" % group_id):
            x_g_id = array_ops.concat(
                [
                    self._get_input_for_group(inputs, group_id, input_group_size),
                    self._get_input_for_group(m_prev, group_id,
                                              self._group_shape[0])
                ],
                axis=1)
            linear = self._linear1[group_id]
            if linear is None:
              linear = _Linear(x_g_id, 4 * self._group_shape[1], False)
              self._linear1[group_id] = linear
            R_k = linear(x_g_id)  # pylint: disable=invalid-name
            i_k, j_k, f_k, o_k = array_ops.split(R_k, 4, 1)
  
          i_parts.append(i_k)
          j_parts.append(j_k)
          f_parts.append(f_k)
          o_parts.append(o_k)
  
        bi = vs.get_variable(
            name="bias_i",
            shape=[self._num_units],
            dtype=dtype,
            initializer=init_ops.constant_initializer(0.0, dtype=dtype))
        bj = vs.get_variable(
            name="bias_j",
            shape=[self._num_units],
            dtype=dtype,
            initializer=init_ops.constant_initializer(0.0, dtype=dtype))
        bf = vs.get_variable(
            name="bias_f",
            shape=[self._num_units],
            dtype=dtype,
            initializer=init_ops.constant_initializer(0.0, dtype=dtype))
        bo = vs.get_variable(
            name="bias_o",
            shape=[self._num_units],
            dtype=dtype,
            initializer=init_ops.constant_initializer(0.0, dtype=dtype))
  
        i = nn_ops.bias_add(array_ops.concat(i_parts, axis=1), bi)
        j = nn_ops.bias_add(array_ops.concat(j_parts, axis=1), bj)
        f = nn_ops.bias_add(array_ops.concat(f_parts, axis=1), bf)
        o = nn_ops.bias_add(array_ops.concat(o_parts, axis=1), bo)
  
      c = (
          math_ops.sigmoid(f + self._forget_bias) * c_prev +
          math_ops.sigmoid(i) * math_ops.tanh(j))
      m = math_ops.sigmoid(o) * self._activation(c)
  
      if self._num_proj is not None:
        with vs.variable_scope("projection"):
          if self._linear2 is None:
            self._linear2 = _Linear(m, self._num_proj, False)
          m = self._linear2(m)
  
      new_state = rnn_cell_impl.LSTMStateTuple(c, m)
      return m, new_state
  
  
  class LayerNormLSTMCell(rnn_cell_impl.RNNCell):
    """Long short-term memory unit (LSTM) recurrent network cell.
  
    The default non-peephole implementation is based on:
  
      http://www.bioinf.jku.at/publications/older/2604.pdf
  
    S. Hochreiter and J. Schmidhuber.
    "Long Short-Term Memory". Neural Computation, 9(8):1735-1780, 1997.
  
    The peephole implementation is based on:
  
      https://research.google.com/pubs/archive/43905.pdf
  
    Hasim Sak, Andrew Senior, and Francoise Beaufays.
    "Long short-term memory recurrent neural network architectures for
     large scale acoustic modeling." INTERSPEECH, 2014.
  
    The class uses optional peep-hole connections, optional cell clipping, and
    an optional projection layer.
  
    Layer normalization implementation is based on:
  
      https://arxiv.org/abs/1607.06450.
  
    "Layer Normalization"
    Jimmy Lei Ba, Jamie Ryan Kiros, Geoffrey E. Hinton
  
    and is applied before the internal nonlinearities.
  
    """
  
    def __init__(self,
                 num_units,
                 use_peepholes=False,
                 cell_clip=None,
                 initializer=None,
                 num_proj=None,
                 proj_clip=None,
                 forget_bias=1.0,
                 activation=None,
                 layer_norm=False,
                 norm_gain=1.0,
                 norm_shift=0.0,
                 reuse=None):
      """Initialize the parameters for an LSTM cell.
  
      Args:
        num_units: int, The number of units in the LSTM cell
        use_peepholes: bool, set True to enable diagonal/peephole connections.
        cell_clip: (optional) A float value, if provided the cell state is clipped
          by this value prior to the cell output activation.
        initializer: (optional) The initializer to use for the weight and
          projection matrices.
        num_proj: (optional) int, The output dimensionality for the projection
          matrices.  If None, no projection is performed.
        proj_clip: (optional) A float value.  If `num_proj > 0` and `proj_clip` is
          provided, then the projected values are clipped elementwise to within
          `[-proj_clip, proj_clip]`.
        forget_bias: Biases of the forget gate are initialized by default to 1
          in order to reduce the scale of forgetting at the beginning of
          the training. Must set it manually to `0.0` when restoring from
          CudnnLSTM trained checkpoints.
        activation: Activation function of the inner states.  Default: `tanh`.
        layer_norm: If `True`, layer normalization will be applied.
        norm_gain: float, The layer normalization gain initial value. If
          `layer_norm` has been set to `False`, this argument will be ignored.
        norm_shift: float, The layer normalization shift initial value. If
          `layer_norm` has been set to `False`, this argument will be ignored.
        reuse: (optional) Python boolean describing whether to reuse variables
          in an existing scope.  If not `True`, and the existing scope already has
          the given variables, an error is raised.
  
        When restoring from CudnnLSTM-trained checkpoints, must use
        CudnnCompatibleLSTMCell instead.
      """
      super(LayerNormLSTMCell, self).__init__(_reuse=reuse)
  
      self._num_units = num_units
      self._use_peepholes = use_peepholes
      self._cell_clip = cell_clip
      self._initializer = initializer
      self._num_proj = num_proj
      self._proj_clip = proj_clip
      self._forget_bias = forget_bias
      self._activation = activation or math_ops.tanh
      self._layer_norm = layer_norm
      self._norm_gain = norm_gain
      self._norm_shift = norm_shift
  
      if num_proj:
        self._state_size = (rnn_cell_impl.LSTMStateTuple(num_units, num_proj))
        self._output_size = num_proj
      else:
        self._state_size = (rnn_cell_impl.LSTMStateTuple(num_units, num_units))
        self._output_size = num_units
  
    @property
    def state_size(self):
      return self._state_size
  
    @property
    def output_size(self):
      return self._output_size
  
    def _linear(self,
                args,
                output_size,
                bias,
                bias_initializer=None,
                kernel_initializer=None,
                layer_norm=False):
      """Linear map: sum_i(args[i] * W[i]), where W[i] is a Variable.
  
      Args:
        args: a 2D Tensor or a list of 2D, batch x n, Tensors.
        output_size: int, second dimension of W[i].
        bias: boolean, whether to add a bias term or not.
        bias_initializer: starting value to initialize the bias
          (default is all zeros).
        kernel_initializer: starting value to initialize the weight.
        layer_norm: boolean, whether to apply layer normalization.
  
  
      Returns:
        A 2D Tensor with shape [batch x output_size] taking value
        sum_i(args[i] * W[i]), where each W[i] is a newly created Variable.
  
      Raises:
        ValueError: if some of the arguments has unspecified or wrong shape.
      """
      if args is None or (nest.is_sequence(args) and not args):
        raise ValueError("`args` must be specified")
      if not nest.is_sequence(args):
        args = [args]
  
      # Calculate the total size of arguments on dimension 1.
      total_arg_size = 0
      shapes = [a.get_shape() for a in args]
      for shape in shapes:
        if shape.ndims != 2:
          raise ValueError("linear is expecting 2D arguments: %s" % shapes)
        if shape[1].value is None:
          raise ValueError("linear expects shape[1] to be provided for shape %s, "
                           "but saw %s" % (shape, shape[1]))
        else:
          total_arg_size += shape[1].value
  
      dtype = [a.dtype for a in args][0]
  
      # Now the computation.
      scope = vs.get_variable_scope()
      with vs.variable_scope(scope) as outer_scope:
        weights = vs.get_variable(
            "kernel", [total_arg_size, output_size],
            dtype=dtype,
            initializer=kernel_initializer)
        if len(args) == 1:
          res = math_ops.matmul(args[0], weights)
        else:
          res = math_ops.matmul(array_ops.concat(args, 1), weights)
        if not bias:
          return res
        with vs.variable_scope(outer_scope) as inner_scope:
          inner_scope.set_partitioner(None)
          if bias_initializer is None:
            bias_initializer = init_ops.constant_initializer(0.0, dtype=dtype)
          biases = vs.get_variable(
              "bias", [output_size], dtype=dtype, initializer=bias_initializer)
  
      if not layer_norm:
        res = nn_ops.bias_add(res, biases)
  
      return res
  
    def call(self, inputs, state):
      """Run one step of LSTM.
  
      Args:
        inputs: input Tensor, 2D, batch x num_units.
        state: this must be a tuple of state Tensors,
         both `2-D`, with column sizes `c_state` and
          `m_state`.
  
      Returns:
        A tuple containing:
  
        - A `2-D, [batch x output_dim]`, Tensor representing the output of the
          LSTM after reading `inputs` when previous state was `state`.
          Here output_dim is:
             num_proj if num_proj was set,
             num_units otherwise.
        - Tensor(s) representing the new state of LSTM after reading `inputs` when
          the previous state was `state`.  Same type and shape(s) as `state`.
  
      Raises:
        ValueError: If input size cannot be inferred from inputs via
          static shape inference.
      """
      sigmoid = math_ops.sigmoid
  
      (c_prev, m_prev) = state
  
      dtype = inputs.dtype
      input_size = inputs.get_shape().with_rank(2)[1]
      if input_size.value is None:
        raise ValueError("Could not infer input size from inputs.get_shape()[-1]")
      scope = vs.get_variable_scope()
      with vs.variable_scope(scope, initializer=self._initializer) as unit_scope:
  
        # i = input_gate, j = new_input, f = forget_gate, o = output_gate
        lstm_matrix = self._linear(
            [inputs, m_prev],
            4 * self._num_units,
            bias=True,
            bias_initializer=None,
            layer_norm=self._layer_norm)
        i, j, f, o = array_ops.split(
            value=lstm_matrix, num_or_size_splits=4, axis=1)
  
        if self._layer_norm:
          i = _norm(self._norm_gain, self._norm_shift, i, "input")
          j = _norm(self._norm_gain, self._norm_shift, j, "transform")
          f = _norm(self._norm_gain, self._norm_shift, f, "forget")
          o = _norm(self._norm_gain, self._norm_shift, o, "output")
  
        # Diagonal connections
        if self._use_peepholes:
          with vs.variable_scope(unit_scope):
            w_f_diag = vs.get_variable(
                "w_f_diag", shape=[self._num_units], dtype=dtype)
            w_i_diag = vs.get_variable(
                "w_i_diag", shape=[self._num_units], dtype=dtype)
            w_o_diag = vs.get_variable(
                "w_o_diag", shape=[self._num_units], dtype=dtype)
  
        if self._use_peepholes:
          c = (
              sigmoid(f + self._forget_bias + w_f_diag * c_prev) * c_prev +
              sigmoid(i + w_i_diag * c_prev) * self._activation(j))
        else:
          c = (
              sigmoid(f + self._forget_bias) * c_prev +
              sigmoid(i) * self._activation(j))
  
        if self._layer_norm:
          c = _norm(self._norm_gain, self._norm_shift, c, "state")
  
        if self._cell_clip is not None:
          # pylint: disable=invalid-unary-operand-type
          c = clip_ops.clip_by_value(c, -self._cell_clip, self._cell_clip)
          # pylint: enable=invalid-unary-operand-type
        if self._use_peepholes:
          m = sigmoid(o + w_o_diag * c) * self._activation(c)
        else:
          m = sigmoid(o) * self._activation(c)
  
        if self._num_proj is not None:
          with vs.variable_scope("projection"):
            m = self._linear(m, self._num_proj, bias=False)
  
          if self._proj_clip is not None:
            # pylint: disable=invalid-unary-operand-type
            m = clip_ops.clip_by_value(m, -self._proj_clip, self._proj_clip)
            # pylint: enable=invalid-unary-operand-type
  
      new_state = (rnn_cell_impl.LSTMStateTuple(c, m))
      return m, new_state
  
  
  class SRUCell(rnn_cell_impl.LayerRNNCell):
    """SRU, Simple Recurrent Unit.
  
       Implementation based on
       Training RNNs as Fast as CNNs (cf. https://arxiv.org/abs/1709.02755).
  
       This variation of RNN cell is characterized by the simplified data
       dependence
       between hidden states of two consecutive time steps. Traditionally, hidden
       states from a cell at time step t-1 needs to be multiplied with a matrix
       W_hh before being fed into the ensuing cell at time step t.
       This flavor of RNN replaces the matrix multiplication between h_{t-1}
       and W_hh with a pointwise multiplication, resulting in performance
       gain.
  
    Args:
      num_units: int, The number of units in the SRU cell.
      activation: Nonlinearity to use.  Default: `tanh`.
      reuse: (optional) Python boolean describing whether to reuse variables
        in an existing scope.  If not `True`, and the existing scope already has
        the given variables, an error is raised.
      name: (optional) String, the name of the layer. Layers with the same name
        will share weights, but to avoid mistakes we require reuse=True in such
        cases.
    """
  
    def __init__(self, num_units, activation=None, reuse=None, name=None):
      super(SRUCell, self).__init__(_reuse=reuse, name=name)
      self._num_units = num_units
      self._activation = activation or math_ops.tanh
  
      # Restrict inputs to be 2-dimensional matrices
      self.input_spec = base_layer.InputSpec(ndim=2)
  
    @property
    def state_size(self):
      return self._num_units
  
    @property
    def output_size(self):
      return self._num_units
  
    def build(self, inputs_shape):
      if inputs_shape[1].value is None:
        raise ValueError(
            "Expected inputs.shape[-1] to be known, saw shape: %s" % inputs_shape)
  
      input_depth = inputs_shape[1].value
  
      # pylint: disable=protected-access
      self._kernel = self.add_variable(
          rnn_cell_impl._WEIGHTS_VARIABLE_NAME,
          shape=[input_depth, 4 * self._num_units])
      # pylint: enable=protected-access
      self._bias = self.add_variable(
          rnn_cell_impl._BIAS_VARIABLE_NAME,  # pylint: disable=protected-access
          shape=[2 * self._num_units],
          initializer=init_ops.constant_initializer(0.0, dtype=self.dtype))
  
      self._built = True
  
    def call(self, inputs, state):
      """Simple recurrent unit (SRU) with num_units cells."""
  
      U = math_ops.matmul(inputs, self._kernel)  # pylint: disable=invalid-name
      x_bar, f_intermediate, r_intermediate, x_tx = array_ops.split(
          value=U, num_or_size_splits=4, axis=1)
  
      f_r = math_ops.sigmoid(
          nn_ops.bias_add(
              array_ops.concat([f_intermediate, r_intermediate], 1), self._bias))
      f, r = array_ops.split(value=f_r, num_or_size_splits=2, axis=1)
  
      c = f * state + (1.0 - f) * x_bar
      h = r * self._activation(c) + (1.0 - r) * x_tx
  
      return h, c
  
  
  class WeightNormLSTMCell(rnn_cell_impl.RNNCell):
    """Weight normalized LSTM Cell. Adapted from `rnn_cell_impl.LSTMCell`.
  
      The weight-norm implementation is based on:
      https://arxiv.org/abs/1602.07868
      Tim Salimans, Diederik P. Kingma.
      Weight Normalization: A Simple Reparameterization to Accelerate
      Training of Deep Neural Networks
  
      The default LSTM implementation based on:
      http://www.bioinf.jku.at/publications/older/2604.pdf
      S. Hochreiter and J. Schmidhuber.
      "Long Short-Term Memory". Neural Computation, 9(8):1735-1780, 1997.
  
      The class uses optional peephole connections, optional cell clipping
      and an optional projection layer.
  
      The optional peephole implementation is based on:
      https://research.google.com/pubs/archive/43905.pdf
      Hasim Sak, Andrew Senior, and Francoise Beaufays.
      "Long short-term memory recurrent neural network architectures for
      large scale acoustic modeling." INTERSPEECH, 2014.
    """
  
    def __init__(self,
                 num_units,
                 norm=True,
                 use_peepholes=False,
                 cell_clip=None,
                 initializer=None,
                 num_proj=None,
                 proj_clip=None,
                 forget_bias=1,
                 activation=None,
                 reuse=None):
      """Initialize the parameters of a weight-normalized LSTM cell.
  
      Args:
        num_units: int, The number of units in the LSTM cell
        norm: If `True`, apply normalization to the weight matrices. If False,
          the result is identical to that obtained from `rnn_cell_impl.LSTMCell`
        use_peepholes: bool, set `True` to enable diagonal/peephole connections.
        cell_clip: (optional) A float value, if provided the cell state is clipped
          by this value prior to the cell output activation.
        initializer: (optional) The initializer to use for the weight matrices.
        num_proj: (optional) int, The output dimensionality for the projection
          matrices.  If None, no projection is performed.
        proj_clip: (optional) A float value.  If `num_proj > 0` and `proj_clip` is
          provided, then the projected values are clipped elementwise to within
          `[-proj_clip, proj_clip]`.
        forget_bias: Biases of the forget gate are initialized by default to 1
          in order to reduce the scale of forgetting at the beginning of
          the training.
        activation: Activation function of the inner states.  Default: `tanh`.
        reuse: (optional) Python boolean describing whether to reuse variables
          in an existing scope.  If not `True`, and the existing scope already has
          the given variables, an error is raised.
      """
      super(WeightNormLSTMCell, self).__init__(_reuse=reuse)
  
      self._scope = "wn_lstm_cell"
      self._num_units = num_units
      self._norm = norm
      self._initializer = initializer
      self._use_peepholes = use_peepholes
      self._cell_clip = cell_clip
      self._num_proj = num_proj
      self._proj_clip = proj_clip
      self._activation = activation or math_ops.tanh
      self._forget_bias = forget_bias
  
      self._weights_variable_name = "kernel"
      self._bias_variable_name = "bias"
  
      if num_proj:
        self._state_size = rnn_cell_impl.LSTMStateTuple(num_units, num_proj)
        self._output_size = num_proj
      else:
        self._state_size = rnn_cell_impl.LSTMStateTuple(num_units, num_units)
        self._output_size = num_units
  
    @property
    def state_size(self):
      return self._state_size
  
    @property
    def output_size(self):
      return self._output_size
  
    def _normalize(self, weight, name):
      """Apply weight normalization.
  
      Args:
        weight: a 2D tensor with known number of columns.
        name: string, variable name for the normalizer.
      Returns:
        A tensor with the same shape as `weight`.
      """
  
      output_size = weight.get_shape().as_list()[1]
      g = vs.get_variable(name, [output_size], dtype=weight.dtype)
      return nn_impl.l2_normalize(weight, axis=0) * g
  
    def _linear(self,
                args,
                output_size,
                norm,
                bias,
                bias_initializer=None,
                kernel_initializer=None):
      """Linear map: sum_i(args[i] * W[i]), where W[i] is a variable.
  
      Args:
        args: a 2D Tensor or a list of 2D, batch x n, Tensors.
        output_size: int, second dimension of W[i].
        norm: bool, whether to normalize the weights.
        bias: boolean, whether to add a bias term or not.
        bias_initializer: starting value to initialize the bias
          (default is all zeros).
        kernel_initializer: starting value to initialize the weight.
  
      Returns:
        A 2D Tensor with shape [batch x output_size] equal to
        sum_i(args[i] * W[i]), where W[i]s are newly created matrices.
  
      Raises:
        ValueError: if some of the arguments has unspecified or wrong shape.
      """
      if args is None or (nest.is_sequence(args) and not args):
        raise ValueError("`args` must be specified")
      if not nest.is_sequence(args):
        args = [args]
  
      # Calculate the total size of arguments on dimension 1.
      total_arg_size = 0
      shapes = [a.get_shape() for a in args]
      for shape in shapes:
        if shape.ndims != 2:
          raise ValueError("linear is expecting 2D arguments: %s" % shapes)
        if shape[1].value is None:
          raise ValueError("linear expects shape[1] to be provided for shape %s, "
                           "but saw %s" % (shape, shape[1]))
        else:
          total_arg_size += shape[1].value
  
      dtype = [a.dtype for a in args][0]
  
      # Now the computation.
      scope = vs.get_variable_scope()
      with vs.variable_scope(scope) as outer_scope:
        weights = vs.get_variable(
            self._weights_variable_name, [total_arg_size, output_size],
            dtype=dtype,
            initializer=kernel_initializer)
        if norm:
          wn = []
          st = 0
          with ops.control_dependencies(None):
            for i in range(len(args)):
              en = st + shapes[i][1].value
              wn.append(
                  self._normalize(weights[st:en, :], name="norm_{}".format(i)))
              st = en
  
            weights = array_ops.concat(wn, axis=0)
  
        if len(args) == 1:
          res = math_ops.matmul(args[0], weights)
        else:
          res = math_ops.matmul(array_ops.concat(args, 1), weights)
        if not bias:
          return res
  
        with vs.variable_scope(outer_scope) as inner_scope:
          inner_scope.set_partitioner(None)
          if bias_initializer is None:
            bias_initializer = init_ops.constant_initializer(0.0, dtype=dtype)
  
          biases = vs.get_variable(
              self._bias_variable_name, [output_size],
              dtype=dtype,
              initializer=bias_initializer)
  
        return nn_ops.bias_add(res, biases)
  
    def call(self, inputs, state):
      """Run one step of LSTM.
  
      Args:
        inputs: input Tensor, 2D, batch x num_units.
        state: A tuple of state Tensors, both `2-D`, with column sizes
         `c_state` and `m_state`.
  
      Returns:
        A tuple containing:
  
        - A `2-D, [batch x output_dim]`, Tensor representing the output of the
          LSTM after reading `inputs` when previous state was `state`.
          Here output_dim is:
             num_proj if num_proj was set,
             num_units otherwise.
        - Tensor(s) representing the new state of LSTM after reading `inputs` when
          the previous state was `state`.  Same type and shape(s) as `state`.
  
      Raises:
        ValueError: If input size cannot be inferred from inputs via
          static shape inference.
      """
      dtype = inputs.dtype
      num_units = self._num_units
      sigmoid = math_ops.sigmoid
      c, h = state
  
      input_size = inputs.get_shape().with_rank(2)[1]
      if input_size.value is None:
        raise ValueError("Could not infer input size from inputs.get_shape()[-1]")
  
      with vs.variable_scope(self._scope, initializer=self._initializer):
  
        concat = self._linear(
            [inputs, h], 4 * num_units, norm=self._norm, bias=True)
  
        # i = input_gate, j = new_input, f = forget_gate, o = output_gate
        i, j, f, o = array_ops.split(value=concat, num_or_size_splits=4, axis=1)
  
        if self._use_peepholes:
          w_f_diag = vs.get_variable("w_f_diag", shape=[num_units], dtype=dtype)
          w_i_diag = vs.get_variable("w_i_diag", shape=[num_units], dtype=dtype)
          w_o_diag = vs.get_variable("w_o_diag", shape=[num_units], dtype=dtype)
  
          new_c = (
              c * sigmoid(f + self._forget_bias + w_f_diag * c) +
              sigmoid(i + w_i_diag * c) * self._activation(j))
        else:
          new_c = (
              c * sigmoid(f + self._forget_bias) +
              sigmoid(i) * self._activation(j))
  
        if self._cell_clip is not None:
          # pylint: disable=invalid-unary-operand-type
          new_c = clip_ops.clip_by_value(new_c, -self._cell_clip, self._cell_clip)
          # pylint: enable=invalid-unary-operand-type
        if self._use_peepholes:
          new_h = sigmoid(o + w_o_diag * new_c) * self._activation(new_c)
        else:
          new_h = sigmoid(o) * self._activation(new_c)
  
        if self._num_proj is not None:
          with vs.variable_scope("projection"):
            new_h = self._linear(
                new_h, self._num_proj, norm=self._norm, bias=False)
  
          if self._proj_clip is not None:
            # pylint: disable=invalid-unary-operand-type
            new_h = clip_ops.clip_by_value(new_h, -self._proj_clip,
                                           self._proj_clip)
            # pylint: enable=invalid-unary-operand-type
  
        new_state = rnn_cell_impl.LSTMStateTuple(new_c, new_h)
        return new_h, new_state
  
  
  class IndRNNCell(rnn_cell_impl.LayerRNNCell):
    """Independently Recurrent Neural Network (IndRNN) cell
      (cf. https://arxiv.org/abs/1803.04831).
  
    Args:
      num_units: int, The number of units in the RNN cell.
      activation: Nonlinearity to use.  Default: `tanh`.
      reuse: (optional) Python boolean describing whether to reuse variables
       in an existing scope.  If not `True`, and the existing scope already has
       the given variables, an error is raised.
      name: String, the name of the layer. Layers with the same name will
        share weights, but to avoid mistakes we require reuse=True in such
        cases.
      dtype: Default dtype of the layer (default of `None` means use the type
        of the first input). Required when `build` is called before `call`.
    """
  
    def __init__(self,
                 num_units,
                 activation=None,
                 reuse=None,
                 name=None,
                 dtype=None):
      super(IndRNNCell, self).__init__(_reuse=reuse, name=name, dtype=dtype)
  
      # Inputs must be 2-dimensional.
      self.input_spec = base_layer.InputSpec(ndim=2)
  
      self._num_units = num_units
      self._activation = activation or math_ops.tanh
  
    @property
    def state_size(self):
      return self._num_units
  
    @property
    def output_size(self):
      return self._num_units
  
    def build(self, inputs_shape):
      if inputs_shape[1].value is None:
        raise ValueError(
            "Expected inputs.shape[-1] to be known, saw shape: %s" % inputs_shape)
  
      input_depth = inputs_shape[1].value
      # pylint: disable=protected-access
      self._kernel_w = self.add_variable(
          "%s_w" % rnn_cell_impl._WEIGHTS_VARIABLE_NAME,
          shape=[input_depth, self._num_units])
      self._kernel_u = self.add_variable(
          "%s_u" % rnn_cell_impl._WEIGHTS_VARIABLE_NAME,
          shape=[1, self._num_units],
          initializer=init_ops.random_uniform_initializer(
              minval=-1, maxval=1, dtype=self.dtype))
      self._bias = self.add_variable(
          rnn_cell_impl._BIAS_VARIABLE_NAME,
          shape=[self._num_units],
          initializer=init_ops.zeros_initializer(dtype=self.dtype))
      # pylint: enable=protected-access
  
      self.built = True
  
    def call(self, inputs, state):
      """IndRNN: output = new_state = act(W * input + u * state + B)."""
  
      gate_inputs = math_ops.matmul(inputs, self._kernel_w) + (
          state * self._kernel_u)
      gate_inputs = nn_ops.bias_add(gate_inputs, self._bias)
      output = self._activation(gate_inputs)
      return output, output
  
  
  class IndyGRUCell(rnn_cell_impl.LayerRNNCell):
    r"""Independently Gated Recurrent Unit cell.
  
    Based on IndRNNs (https://arxiv.org/abs/1803.04831) and similar to GRUCell,
    yet with the \(U_r\), \(U_z\), and \(U\) matrices in equations 5, 6, and
    8 of http://arxiv.org/abs/1406.1078 respectively replaced by diagonal
    matrices, i.e. a Hadamard product with a single vector:
  
      $$r_j = \sigma\left([\mathbf W_r\mathbf x]_j +
        [\mathbf u_r\circ \mathbf h_{(t-1)}]_j\right)$$
      $$z_j = \sigma\left([\mathbf W_z\mathbf x]_j +
        [\mathbf u_z\circ \mathbf h_{(t-1)}]_j\right)$$
      $$\tilde{h}^{(t)}_j = \phi\left([\mathbf W \mathbf x]_j +
        [\mathbf u \circ \mathbf r \circ \mathbf h_{(t-1)}]_j\right)$$
  
    where \(\circ\) denotes the Hadamard operator. This means that each IndyGRU
    node sees only its own state, as opposed to seeing all states in the same
    layer.
  
    TODO(gonnet): Write a paper describing this and add a reference here.
  
    Args:
      num_units: int, The number of units in the GRU cell.
      activation: Nonlinearity to use.  Default: `tanh`.
      reuse: (optional) Python boolean describing whether to reuse variables
       in an existing scope.  If not `True`, and the existing scope already has
       the given variables, an error is raised.
      kernel_initializer: (optional) The initializer to use for the weight
        matrices applied to the input.
      bias_initializer: (optional) The initializer to use for the bias.
      name: String, the name of the layer. Layers with the same name will
        share weights, but to avoid mistakes we require reuse=True in such
        cases.
      dtype: Default dtype of the layer (default of `None` means use the type
        of the first input). Required when `build` is called before `call`.
    """
  
    def __init__(self,
                 num_units,
                 activation=None,
                 reuse=None,
                 kernel_initializer=None,
                 bias_initializer=None,
                 name=None,
                 dtype=None):
      super(IndyGRUCell, self).__init__(_reuse=reuse, name=name, dtype=dtype)
  
      # Inputs must be 2-dimensional.
      self.input_spec = base_layer.InputSpec(ndim=2)
  
      self._num_units = num_units
      self._activation = activation or math_ops.tanh
      self._kernel_initializer = kernel_initializer
      self._bias_initializer = bias_initializer
  
    @property
    def state_size(self):
      return self._num_units
  
    @property
    def output_size(self):
      return self._num_units
  
    def build(self, inputs_shape):
      if inputs_shape[1].value is None:
        raise ValueError(
            "Expected inputs.shape[-1] to be known, saw shape: %s" % inputs_shape)
  
      input_depth = inputs_shape[1].value
      # pylint: disable=protected-access
      self._gate_kernel_w = self.add_variable(
          "gates/%s_w" % rnn_cell_impl._WEIGHTS_VARIABLE_NAME,
          shape=[input_depth, 2 * self._num_units],
          initializer=self._kernel_initializer)
      self._gate_kernel_u = self.add_variable(
          "gates/%s_u" % rnn_cell_impl._WEIGHTS_VARIABLE_NAME,
          shape=[1, 2 * self._num_units],
          initializer=init_ops.random_uniform_initializer(
              minval=-1, maxval=1, dtype=self.dtype))
      self._gate_bias = self.add_variable(
          "gates/%s" % rnn_cell_impl._BIAS_VARIABLE_NAME,
          shape=[2 * self._num_units],
          initializer=(self._bias_initializer
                       if self._bias_initializer is not None else
                       init_ops.constant_initializer(1.0, dtype=self.dtype)))
      self._candidate_kernel_w = self.add_variable(
          "candidate/%s" % rnn_cell_impl._WEIGHTS_VARIABLE_NAME,
          shape=[input_depth, self._num_units],
          initializer=self._kernel_initializer)
      self._candidate_kernel_u = self.add_variable(
          "candidate/%s_u" % rnn_cell_impl._WEIGHTS_VARIABLE_NAME,
          shape=[1, self._num_units],
          initializer=init_ops.random_uniform_initializer(
              minval=-1, maxval=1, dtype=self.dtype))
      self._candidate_bias = self.add_variable(
          "candidate/%s" % rnn_cell_impl._BIAS_VARIABLE_NAME,
          shape=[self._num_units],
          initializer=(self._bias_initializer
                       if self._bias_initializer is not None else
                       init_ops.zeros_initializer(dtype=self.dtype)))
      # pylint: enable=protected-access
  
      self.built = True
  
    def call(self, inputs, state):
      """Gated recurrent unit (GRU) with nunits cells."""
  
      gate_inputs = math_ops.matmul(inputs, self._gate_kernel_w) + (
          gen_array_ops.tile(state, [1, 2]) * self._gate_kernel_u)
      gate_inputs = nn_ops.bias_add(gate_inputs, self._gate_bias)
  
      value = math_ops.sigmoid(gate_inputs)
      r, u = array_ops.split(value=value, num_or_size_splits=2, axis=1)
  
      r_state = r * state
  
      candidate = math_ops.matmul(inputs, self._candidate_kernel_w) + (
          r_state * self._candidate_kernel_u)
      candidate = nn_ops.bias_add(candidate, self._candidate_bias)
  
      c = self._activation(candidate)
      new_h = u * state + (1 - u) * c
      return new_h, new_h
  
  
  class IndyLSTMCell(rnn_cell_impl.LayerRNNCell):
    r"""Basic IndyLSTM recurrent network cell.
  
    Based on IndRNNs (https://arxiv.org/abs/1803.04831) and similar to
    BasicLSTMCell, yet with the \(U_f\), \(U_i\), \(U_o\) and \(U_c\)
    matrices in
    https://en.wikipedia.org/wiki/Long_short-term_memory#LSTM_with_a_forget_gate
    replaced by diagonal matrices, i.e. a Hadamard product with a single vector:
  
      $$f_t = \sigma_g\left(W_f x_t + u_f \circ h_{t-1} + b_f\right)$$
      $$i_t = \sigma_g\left(W_i x_t + u_i \circ h_{t-1} + b_i\right)$$
      $$o_t = \sigma_g\left(W_o x_t + u_o \circ h_{t-1} + b_o\right)$$
      $$c_t = f_t \circ c_{t-1} +
              i_t \circ \sigma_c\left(W_c x_t + u_c \circ h_{t-1} + b_c\right)$$
  
    where \(\circ\) denotes the Hadamard operator. This means that each IndyLSTM
    node sees only its own state \(h\) and \(c\), as opposed to seeing all
    states in the same layer.
  
    We add forget_bias (default: 1) to the biases of the forget gate in order to
    reduce the scale of forgetting in the beginning of the training.
  
    It does not allow cell clipping, a projection layer, and does not
    use peep-hole connections: it is the basic baseline.
  
    For advanced models, please use the full `tf.nn.rnn_cell.LSTMCell`
    that follows.
  
    TODO(gonnet): Write a paper describing this and add a reference here.
    """
  
    def __init__(self,
                 num_units,
                 forget_bias=1.0,
                 activation=None,
                 reuse=None,
                 kernel_initializer=None,
                 bias_initializer=None,
                 name=None,
                 dtype=None):
      """Initialize the IndyLSTM cell.
  
      Args:
        num_units: int, The number of units in the LSTM cell.
        forget_bias: float, The bias added to forget gates (see above).
          Must set to `0.0` manually when restoring from CudnnLSTM-trained
          checkpoints.
        activation: Activation function of the inner states.  Default: `tanh`.
        reuse: (optional) Python boolean describing whether to reuse variables
          in an existing scope.  If not `True`, and the existing scope already has
          the given variables, an error is raised.
        kernel_initializer: (optional) The initializer to use for the weight
          matrix applied to the inputs.
        bias_initializer: (optional) The initializer to use for the bias.
        name: String, the name of the layer. Layers with the same name will
          share weights, but to avoid mistakes we require reuse=True in such
          cases.
        dtype: Default dtype of the layer (default of `None` means use the type
          of the first input). Required when `build` is called before `call`.
      """
      super(IndyLSTMCell, self).__init__(_reuse=reuse, name=name, dtype=dtype)
  
      # Inputs must be 2-dimensional.
      self.input_spec = base_layer.InputSpec(ndim=2)
  
      self._num_units = num_units
      self._forget_bias = forget_bias
      self._activation = activation or math_ops.tanh
      self._kernel_initializer = kernel_initializer
      self._bias_initializer = bias_initializer
  
    @property
    def state_size(self):
      return rnn_cell_impl.LSTMStateTuple(self._num_units, self._num_units)
  
    @property
    def output_size(self):
      return self._num_units
  
    def build(self, inputs_shape):
      if inputs_shape[1].value is None:
        raise ValueError(
            "Expected inputs.shape[-1] to be known, saw shape: %s" % inputs_shape)
  
      input_depth = inputs_shape[1].value
      # pylint: disable=protected-access
      self._kernel_w = self.add_variable(
          "%s_w" % rnn_cell_impl._WEIGHTS_VARIABLE_NAME,
          shape=[input_depth, 4 * self._num_units],
          initializer=self._kernel_initializer)
      self._kernel_u = self.add_variable(
          "%s_u" % rnn_cell_impl._WEIGHTS_VARIABLE_NAME,
          shape=[1, 4 * self._num_units],
          initializer=init_ops.random_uniform_initializer(
              minval=-1, maxval=1, dtype=self.dtype))
      self._bias = self.add_variable(
          rnn_cell_impl._BIAS_VARIABLE_NAME,
          shape=[4 * self._num_units],
          initializer=(self._bias_initializer
                       if self._bias_initializer is not None else
                       init_ops.zeros_initializer(dtype=self.dtype)))
      # pylint: enable=protected-access
  
      self.built = True
  
    def call(self, inputs, state):
      """Independent Long short-term memory cell (IndyLSTM).
  
      Args:
        inputs: `2-D` tensor with shape `[batch_size, input_size]`.
        state: An `LSTMStateTuple` of state tensors, each shaped
          `[batch_size, num_units]`.
  
      Returns:
        A pair containing the new hidden state, and the new state (a
          `LSTMStateTuple`).
      """
      sigmoid = math_ops.sigmoid
      one = constant_op.constant(1, dtype=dtypes.int32)
      c, h = state
  
      gate_inputs = math_ops.matmul(inputs, self._kernel_w)
      gate_inputs += gen_array_ops.tile(h, [1, 4]) * self._kernel_u
      gate_inputs = nn_ops.bias_add(gate_inputs, self._bias)
  
      # i = input_gate, j = new_input, f = forget_gate, o = output_gate
      i, j, f, o = array_ops.split(
          value=gate_inputs, num_or_size_splits=4, axis=one)
  
      forget_bias_tensor = constant_op.constant(self._forget_bias, dtype=f.dtype)
      # Note that using `add` and `multiply` instead of `+` and `*` gives a
      # performance improvement. So using those at the cost of readability.
      add = math_ops.add
      multiply = math_ops.multiply
      new_c = add(
          multiply(c, sigmoid(add(f, forget_bias_tensor))),
          multiply(sigmoid(i), self._activation(j)))
      new_h = multiply(self._activation(new_c), sigmoid(o))
  
      new_state = rnn_cell_impl.LSTMStateTuple(new_c, new_h)
      return new_h, new_state
   # 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 tensorflow.ops.image_ops."""
  
  from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  
  import colorsys
  import functools
  import itertools
  import math
  import os
  import time
  
  import numpy as np
  from six.moves import xrange  # pylint: disable=redefined-builtin
  
  from tensorflow.core.protobuf import config_pb2
  from tensorflow.python.client import session
  from tensorflow.python.framework import constant_op
  from tensorflow.python.framework import dtypes
  from tensorflow.python.framework import errors
  from tensorflow.python.framework import ops
  from tensorflow.python.framework import test_util
  from tensorflow.python.ops import array_ops
  from tensorflow.python.ops import control_flow_ops
  from tensorflow.python.ops import gen_image_ops
  from tensorflow.python.ops import gradients
  from tensorflow.python.ops import image_ops
  from tensorflow.python.ops import image_ops_impl
  from tensorflow.python.ops import io_ops
  from tensorflow.python.ops import math_ops
  from tensorflow.python.ops import random_ops
  from tensorflow.python.ops import variables
  from tensorflow.python.platform import googletest
  from tensorflow.python.platform import test
  
  
  class RGBToHSVTest(test_util.TensorFlowTestCase):
  
    def testBatch(self):
      # Build an arbitrary RGB image
      np.random.seed(7)
      batch_size = 5
      shape = (batch_size, 2, 7, 3)
  
      for nptype in [np.float32, np.float64]:
        inp = np.random.rand(*shape).astype(nptype)
  
        # Convert to HSV and back, as a batch and individually
        with self.test_session(use_gpu=True) as sess:
          batch0 = constant_op.constant(inp)
          batch1 = image_ops.rgb_to_hsv(batch0)
          batch2 = image_ops.hsv_to_rgb(batch1)
          split0 = array_ops.unstack(batch0)
          split1 = list(map(image_ops.rgb_to_hsv, split0))
          split2 = list(map(image_ops.hsv_to_rgb, split1))
          join1 = array_ops.stack(split1)
          join2 = array_ops.stack(split2)
          batch1, batch2, join1, join2 = sess.run([batch1, batch2, join1, join2])
  
        # Verify that processing batch elements together is the same as separate
        self.assertAllClose(batch1, join1)
        self.assertAllClose(batch2, join2)
        self.assertAllClose(batch2, inp)
  
    def testRGBToHSVRoundTrip(self):
      data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      for nptype in [np.float32, np.float64]:
        rgb_np = np.array(data, dtype=nptype).reshape([2, 2, 3]) / 255.
        with self.test_session(use_gpu=True):
          hsv = image_ops.rgb_to_hsv(rgb_np)
          rgb = image_ops.hsv_to_rgb(hsv)
          rgb_tf = rgb.eval()
        self.assertAllClose(rgb_tf, rgb_np)
  
  
  class RGBToYIQTest(test_util.TensorFlowTestCase):
  
    def testBatch(self):
      # Build an arbitrary RGB image
      np.random.seed(7)
      batch_size = 5
      shape = (batch_size, 2, 7, 3)
  
      for nptype in [np.float32, np.float64]:
        inp = np.random.rand(*shape).astype(nptype)
  
        # Convert to YIQ and back, as a batch and individually
        with self.test_session(use_gpu=True) as sess:
          batch0 = constant_op.constant(inp)
          batch1 = image_ops.rgb_to_yiq(batch0)
          batch2 = image_ops.yiq_to_rgb(batch1)
          split0 = array_ops.unstack(batch0)
          split1 = list(map(image_ops.rgb_to_yiq, split0))
          split2 = list(map(image_ops.yiq_to_rgb, split1))
          join1 = array_ops.stack(split1)
          join2 = array_ops.stack(split2)
          batch1, batch2, join1, join2 = sess.run([batch1, batch2, join1, join2])
  
        # Verify that processing batch elements together is the same as separate
        self.assertAllClose(batch1, join1, rtol=1e-4, atol=1e-4)
        self.assertAllClose(batch2, join2, rtol=1e-4, atol=1e-4)
        self.assertAllClose(batch2, inp, rtol=1e-4, atol=1e-4)
  
  
  class RGBToYUVTest(test_util.TensorFlowTestCase):
  
    def testBatch(self):
      # Build an arbitrary RGB image
      np.random.seed(7)
      batch_size = 5
      shape = (batch_size, 2, 7, 3)
  
      for nptype in [np.float32, np.float64]:
        inp = np.random.rand(*shape).astype(nptype)
  
        # Convert to YUV and back, as a batch and individually
        with self.test_session(use_gpu=True) as sess:
          batch0 = constant_op.constant(inp)
          batch1 = image_ops.rgb_to_yuv(batch0)
          batch2 = image_ops.yuv_to_rgb(batch1)
          split0 = array_ops.unstack(batch0)
          split1 = list(map(image_ops.rgb_to_yuv, split0))
          split2 = list(map(image_ops.yuv_to_rgb, split1))
          join1 = array_ops.stack(split1)
          join2 = array_ops.stack(split2)
          batch1, batch2, join1, join2 = sess.run([batch1, batch2, join1, join2])
  
        # Verify that processing batch elements together is the same as separate
        self.assertAllClose(batch1, join1, rtol=1e-4, atol=1e-4)
        self.assertAllClose(batch2, join2, rtol=1e-4, atol=1e-4)
        self.assertAllClose(batch2, inp, rtol=1e-4, atol=1e-4)
  
  
  class GrayscaleToRGBTest(test_util.TensorFlowTestCase):
  
    def _RGBToGrayscale(self, images):
      is_batch = True
      if len(images.shape) == 3:
        is_batch = False
        images = np.expand_dims(images, axis=0)
      out_shape = images.shape[0:3] + (1,)
      out = np.zeros(shape=out_shape, dtype=np.uint8)
      for batch in xrange(images.shape[0]):
        for y in xrange(images.shape[1]):
          for x in xrange(images.shape[2]):
            red = images[batch, y, x, 0]
            green = images[batch, y, x, 1]
            blue = images[batch, y, x, 2]
            gray = 0.2989 * red + 0.5870 * green + 0.1140 * blue
            out[batch, y, x, 0] = int(gray)
      if not is_batch:
        out = np.squeeze(out, axis=0)
      return out
  
    def _TestRGBToGrayscale(self, x_np):
      y_np = self._RGBToGrayscale(x_np)
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.rgb_to_grayscale(x_tf)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def testBasicRGBToGrayscale(self):
      # 4-D input with batch dimension.
      x_np = np.array(
          [[1, 2, 3], [4, 10, 1]], dtype=np.uint8).reshape([1, 1, 2, 3])
      self._TestRGBToGrayscale(x_np)
  
      # 3-D input with no batch dimension.
      x_np = np.array([[1, 2, 3], [4, 10, 1]], dtype=np.uint8).reshape([1, 2, 3])
      self._TestRGBToGrayscale(x_np)
  
    def testBasicGrayscaleToRGB(self):
      # 4-D input with batch dimension.
      x_np = np.array([[1, 2]], dtype=np.uint8).reshape([1, 1, 2, 1])
      y_np = np.array(
          [[1, 1, 1], [2, 2, 2]], dtype=np.uint8).reshape([1, 1, 2, 3])
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.grayscale_to_rgb(x_tf)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
      # 3-D input with no batch dimension.
      x_np = np.array([[1, 2]], dtype=np.uint8).reshape([1, 2, 1])
      y_np = np.array([[1, 1, 1], [2, 2, 2]], dtype=np.uint8).reshape([1, 2, 3])
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.grayscale_to_rgb(x_tf)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def testShapeInference(self):
      # Shape inference works and produces expected output where possible
      rgb_shape = [7, None, 19, 3]
      gray_shape = rgb_shape[:-1] + [1]
      with self.test_session(use_gpu=True):
        rgb_tf = array_ops.placeholder(dtypes.uint8, shape=rgb_shape)
        gray = image_ops.rgb_to_grayscale(rgb_tf)
        self.assertEqual(gray_shape, gray.get_shape().as_list())
  
      with self.test_session(use_gpu=True):
        gray_tf = array_ops.placeholder(dtypes.uint8, shape=gray_shape)
        rgb = image_ops.grayscale_to_rgb(gray_tf)
        self.assertEqual(rgb_shape, rgb.get_shape().as_list())
  
      # Shape inference does not break for unknown shapes
      with self.test_session(use_gpu=True):
        rgb_tf_unknown = array_ops.placeholder(dtypes.uint8)
        gray_unknown = image_ops.rgb_to_grayscale(rgb_tf_unknown)
        self.assertFalse(gray_unknown.get_shape())
  
      with self.test_session(use_gpu=True):
        gray_tf_unknown = array_ops.placeholder(dtypes.uint8)
        rgb_unknown = image_ops.grayscale_to_rgb(gray_tf_unknown)
        self.assertFalse(rgb_unknown.get_shape())
  
  
  class AdjustGamma(test_util.TensorFlowTestCase):
  
    def test_adjust_gamma_one(self):
      """Same image should be returned for gamma equal to one"""
      with self.cached_session():
        x_data = np.random.uniform(0, 255, (8, 8))
        x_np = np.array(x_data, dtype=np.float32)
  
        x = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.adjust_gamma(x, gamma=1)
  
        y_tf = y.eval()
        y_np = x_np
  
        self.assertAllClose(y_tf, y_np, 1e-6)
  
    def test_adjust_gamma_less_zero(self):
      """White image should be returned for gamma equal to zero"""
      with self.cached_session():
        x_data = np.random.uniform(0, 255, (8, 8))
        x_np = np.array(x_data, dtype=np.float32)
  
        x = constant_op.constant(x_np, shape=x_np.shape)
  
        err_msg = "Gamma should be a non-negative real number."
  
        try:
          image_ops.adjust_gamma(x, gamma=-1)
        except Exception as e:
          if err_msg not in str(e):
            raise
        else:
          raise AssertionError("Exception not raised: %s" % err_msg)
  
    def test_adjust_gamma_less_zero_tensor(self):
      """White image should be returned for gamma equal to zero"""
      with self.cached_session():
        x_data = np.random.uniform(0, 255, (8, 8))
        x_np = np.array(x_data, dtype=np.float32)
  
        x = constant_op.constant(x_np, shape=x_np.shape)
        y = constant_op.constant(-1.0, dtype=dtypes.float32)
  
        image = image_ops.adjust_gamma(x, gamma=y)
  
        err_msg = "Gamma should be a non-negative real number."
        try:
          image.eval()
        except Exception as e:
          if err_msg not in str(e):
            raise
        else:
          raise AssertionError("Exception not raised: %s" % err_msg)
  
    def test_adjust_gamma_zero(self):
      """White image should be returned for gamma equal to zero"""
      with self.cached_session():
        x_data = np.random.uniform(0, 255, (8, 8))
        x_np = np.array(x_data, dtype=np.float32)
  
        x = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.adjust_gamma(x, gamma=0)
  
        y_tf = y.eval()
  
        dtype = x.dtype.as_numpy_dtype
        y_np = np.array([dtypes.dtype_range[dtype][1]] * x_np.size)
        y_np = y_np.reshape((8, 8))
  
        self.assertAllClose(y_tf, y_np, 1e-6)
  
    def test_adjust_gamma_less_one(self):
      """Verifying the output with expected results for gamma
      correction with gamma equal to half"""
      with self.cached_session():
        x_np = np.arange(0, 255, 4, np.uint8).reshape(8, 8)
        y = image_ops.adjust_gamma(x_np, gamma=0.5)
        y_tf = np.trunc(y.eval())
  
        y_np = np.array(
            [[0, 31, 45, 55, 63, 71, 78, 84], [
                90, 95, 100, 105, 110, 115, 119, 123
            ], [127, 131, 135, 139, 142, 146, 149, 153], [
                156, 159, 162, 165, 168, 171, 174, 177
            ], [180, 183, 186, 188, 191, 194, 196, 199], [
                201, 204, 206, 209, 211, 214, 216, 218
            ], [221, 223, 225, 228, 230, 232, 234, 236],
             [238, 241, 243, 245, 247, 249, 251, 253]],
            dtype=np.float32)
  
        self.assertAllClose(y_tf, y_np, 1e-6)
  
    def test_adjust_gamma_greater_one(self):
      """Verifying the output with expected results for gamma
      correction with gamma equal to two"""
      with self.cached_session():
        x_np = np.arange(0, 255, 4, np.uint8).reshape(8, 8)
        y = image_ops.adjust_gamma(x_np, gamma=2)
        y_tf = np.trunc(y.eval())
  
        y_np = np.array(
            [[0, 0, 0, 0, 1, 1, 2, 3], [4, 5, 6, 7, 9, 10, 12, 14], [
                16, 18, 20, 22, 25, 27, 30, 33
            ], [36, 39, 42, 45, 49, 52, 56, 60], [64, 68, 72, 76, 81, 85, 90, 95],
             [100, 105, 110, 116, 121, 127, 132, 138], [
                 144, 150, 156, 163, 169, 176, 182, 189
             ], [196, 203, 211, 218, 225, 233, 241, 249]],
            dtype=np.float32)
  
        self.assertAllClose(y_tf, y_np, 1e-6)
  
  
  class AdjustHueTest(test_util.TensorFlowTestCase):
  
    def testAdjustNegativeHue(self):
      x_shape = [2, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
  
      delta = -0.25
      y_data = [0, 13, 1, 54, 226, 59, 8, 234, 150, 255, 39, 1]
      y_np = np.array(y_data, dtype=np.uint8).reshape(x_shape)
  
      with self.test_session(use_gpu=True):
        x = constant_op.constant(x_np, shape=x_shape)
        y = image_ops.adjust_hue(x, delta)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def testAdjustPositiveHue(self):
      x_shape = [2, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
  
      delta = 0.25
      y_data = [13, 0, 11, 226, 54, 221, 234, 8, 92, 1, 217, 255]
      y_np = np.array(y_data, dtype=np.uint8).reshape(x_shape)
  
      with self.test_session(use_gpu=True):
        x = constant_op.constant(x_np, shape=x_shape)
        y = image_ops.adjust_hue(x, delta)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def testBatchAdjustHue(self):
      x_shape = [2, 1, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
  
      delta = 0.25
      y_data = [13, 0, 11, 226, 54, 221, 234, 8, 92, 1, 217, 255]
      y_np = np.array(y_data, dtype=np.uint8).reshape(x_shape)
  
      with self.test_session(use_gpu=True):
        x = constant_op.constant(x_np, shape=x_shape)
        y = image_ops.adjust_hue(x, delta)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def _adjustHueNp(self, x_np, delta_h):
      self.assertEqual(x_np.shape[-1], 3)
      x_v = x_np.reshape([-1, 3])
      y_v = np.ndarray(x_v.shape, dtype=x_v.dtype)
      channel_count = x_v.shape[0]
      for i in xrange(channel_count):
        r = x_v[i][0]
        g = x_v[i][1]
        b = x_v[i][2]
        h, s, v = colorsys.rgb_to_hsv(r, g, b)
        h += delta_h
        h = math.fmod(h + 10.0, 1.0)
        r, g, b = colorsys.hsv_to_rgb(h, s, v)
        y_v[i][0] = r
        y_v[i][1] = g
        y_v[i][2] = b
      return y_v.reshape(x_np.shape)
  
    def _adjustHueTf(self, x_np, delta_h):
      with self.test_session(use_gpu=True):
        x = constant_op.constant(x_np)
        y = image_ops.adjust_hue(x, delta_h)
        y_tf = y.eval()
      return y_tf
  
    def testAdjustRandomHue(self):
      x_shapes = [
          [2, 2, 3],
          [4, 2, 3],
          [2, 4, 3],
          [2, 5, 3],
          [1000, 1, 3],
      ]
      test_styles = [
          "all_random",
          "rg_same",
          "rb_same",
          "gb_same",
          "rgb_same",
      ]
      for x_shape in x_shapes:
        for test_style in test_styles:
          x_np = np.random.rand(*x_shape) * 255.
          delta_h = np.random.rand() * 2.0 - 1.0
          if test_style == "all_random":
            pass
          elif test_style == "rg_same":
            x_np[..., 1] = x_np[..., 0]
          elif test_style == "rb_same":
            x_np[..., 2] = x_np[..., 0]
          elif test_style == "gb_same":
            x_np[..., 2] = x_np[..., 1]
          elif test_style == "rgb_same":
            x_np[..., 1] = x_np[..., 0]
            x_np[..., 2] = x_np[..., 0]
          else:
            raise AssertionError("Invalid test style: %s" % (test_style))
          y_np = self._adjustHueNp(x_np, delta_h)
          y_tf = self._adjustHueTf(x_np, delta_h)
          self.assertAllClose(y_tf, y_np, rtol=2e-5, atol=1e-5)
  
    def testInvalidShapes(self):
      fused = False
      if not fused:
        # The tests are known to pass with the fused adjust_hue. We will enable
        # them when the fused implementation is the default.
        return
      x_np = np.random.rand(2, 3) * 255.
      delta_h = np.random.rand() * 2.0 - 1.0
      fused = False
      with self.assertRaisesRegexp(ValueError, "Shape must be at least rank 3"):
        self._adjustHueTf(x_np, delta_h)
      x_np = np.random.rand(4, 2, 4) * 255.
      delta_h = np.random.rand() * 2.0 - 1.0
      with self.assertRaisesOpError("input must have 3 channels"):
        self._adjustHueTf(x_np, delta_h)
  
  
  class FlipImageBenchmark(test.Benchmark):
  
    def _benchmarkFlipLeftRight(self, device, cpu_count):
      image_shape = [299, 299, 3]
      warmup_rounds = 100
      benchmark_rounds = 1000
      config = config_pb2.ConfigProto()
      if cpu_count is not None:
        config.inter_op_parallelism_threads = 1
        config.intra_op_parallelism_threads = cpu_count
      with session.Session("", graph=ops.Graph(), config=config) as sess:
        with ops.device(device):
          inputs = variables.Variable(
              random_ops.random_uniform(image_shape, dtype=dtypes.float32) * 255,
              trainable=False,
              dtype=dtypes.float32)
          run_op = image_ops.flip_left_right(inputs)
          sess.run(variables.global_variables_initializer())
          for i in xrange(warmup_rounds + benchmark_rounds):
            if i == warmup_rounds:
              start = time.time()
            sess.run(run_op)
      end = time.time()
      step_time = (end - start) / benchmark_rounds
      tag = device + "_%s" % (cpu_count if cpu_count is not None else "_all")
      print("benchmarkFlipLeftRight_299_299_3_%s step_time: %.2f us" %
            (tag, step_time * 1e6))
      self.report_benchmark(
          name="benchmarkFlipLeftRight_299_299_3_%s" % (tag),
          iters=benchmark_rounds,
          wall_time=step_time)
  
    def _benchmarkRandomFlipLeftRight(self, device, cpu_count):
      image_shape = [299, 299, 3]
      warmup_rounds = 100
      benchmark_rounds = 1000
      config = config_pb2.ConfigProto()
      if cpu_count is not None:
        config.inter_op_parallelism_threads = 1
        config.intra_op_parallelism_threads = cpu_count
      with session.Session("", graph=ops.Graph(), config=config) as sess:
        with ops.device(device):
          inputs = variables.Variable(
              random_ops.random_uniform(image_shape, dtype=dtypes.float32) * 255,
              trainable=False,
              dtype=dtypes.float32)
          run_op = image_ops.random_flip_left_right(inputs)
          sess.run(variables.global_variables_initializer())
          for i in xrange(warmup_rounds + benchmark_rounds):
            if i == warmup_rounds:
              start = time.time()
            sess.run(run_op)
      end = time.time()
      step_time = (end - start) / benchmark_rounds
      tag = device + "_%s" % (cpu_count if cpu_count is not None else "_all")
      print("benchmarkRandomFlipLeftRight_299_299_3_%s step_time: %.2f us" %
            (tag, step_time * 1e6))
      self.report_benchmark(
          name="benchmarkRandomFlipLeftRight_299_299_3_%s" % (tag),
          iters=benchmark_rounds,
          wall_time=step_time)
  
    def _benchmarkBatchedRandomFlipLeftRight(self, device, cpu_count):
      image_shape = [16, 299, 299, 3]
      warmup_rounds = 100
      benchmark_rounds = 1000
      config = config_pb2.ConfigProto()
      if cpu_count is not None:
        config.inter_op_parallelism_threads = 1
        config.intra_op_parallelism_threads = cpu_count
      with session.Session("", graph=ops.Graph(), config=config) as sess:
        with ops.device(device):
          inputs = variables.Variable(
              random_ops.random_uniform(image_shape, dtype=dtypes.float32) * 255,
              trainable=False,
              dtype=dtypes.float32)
          run_op = image_ops.random_flip_left_right(inputs)
          sess.run(variables.global_variables_initializer())
          for i in xrange(warmup_rounds + benchmark_rounds):
            if i == warmup_rounds:
              start = time.time()
            sess.run(run_op)
      end = time.time()
      step_time = (end - start) / benchmark_rounds
      tag = device + "_%s" % (cpu_count if cpu_count is not None else "_all")
      print("benchmarkBatchedRandomFlipLeftRight_16_299_299_3_%s step_time: "
            "%.2f us" %
            (tag, step_time * 1e6))
      self.report_benchmark(
          name="benchmarkBatchedRandomFlipLeftRight_16_299_299_3_%s" % (tag),
          iters=benchmark_rounds,
          wall_time=step_time)
  
    def benchmarkFlipLeftRightCpu1(self):
      self._benchmarkFlipLeftRight("/cpu:0", 1)
  
    def benchmarkFlipLeftRightCpuAll(self):
      self._benchmarkFlipLeftRight("/cpu:0", None)
  
    def benchmarkFlipLeftRightGpu(self):
      self._benchmarkFlipLeftRight(test.gpu_device_name(), None)
  
    def benchmarkRandomFlipLeftRightCpu1(self):
      self._benchmarkRandomFlipLeftRight("/cpu:0", 1)
  
    def benchmarkRandomFlipLeftRightCpuAll(self):
      self._benchmarkRandomFlipLeftRight("/cpu:0", None)
  
    def benchmarkRandomFlipLeftRightGpu(self):
      self._benchmarkRandomFlipLeftRight(test.gpu_device_name(), None)
  
    def benchmarkBatchedRandomFlipLeftRightCpu1(self):
      self._benchmarkBatchedRandomFlipLeftRight("/cpu:0", 1)
  
    def benchmarkBatchedRandomFlipLeftRightCpuAll(self):
      self._benchmarkBatchedRandomFlipLeftRight("/cpu:0", None)
  
    def benchmarkBatchedRandomFlipLeftRightGpu(self):
      self._benchmarkBatchedRandomFlipLeftRight(test.gpu_device_name(), None)
  
  
  class AdjustHueBenchmark(test.Benchmark):
  
    def _benchmarkAdjustHue(self, device, cpu_count):
      image_shape = [299, 299, 3]
      warmup_rounds = 100
      benchmark_rounds = 1000
      config = config_pb2.ConfigProto()
      if cpu_count is not None:
        config.inter_op_parallelism_threads = 1
        config.intra_op_parallelism_threads = cpu_count
      with session.Session("", graph=ops.Graph(), config=config) as sess:
        with ops.device(device):
          inputs = variables.Variable(
              random_ops.random_uniform(image_shape, dtype=dtypes.float32) * 255,
              trainable=False,
              dtype=dtypes.float32)
          delta = constant_op.constant(0.1, dtype=dtypes.float32)
          outputs = image_ops.adjust_hue(inputs, delta)
          run_op = control_flow_ops.group(outputs)
          sess.run(variables.global_variables_initializer())
          for i in xrange(warmup_rounds + benchmark_rounds):
            if i == warmup_rounds:
              start = time.time()
            sess.run(run_op)
      end = time.time()
      step_time = (end - start) / benchmark_rounds
      tag = device + "_%s" % (cpu_count if cpu_count is not None else "_all")
      print("benchmarkAdjustHue_299_299_3_%s step_time: %.2f us" %
            (tag, step_time * 1e6))
      self.report_benchmark(
          name="benchmarkAdjustHue_299_299_3_%s" % (tag),
          iters=benchmark_rounds,
          wall_time=step_time)
  
    def benchmarkAdjustHueCpu1(self):
      self._benchmarkAdjustHue("/cpu:0", 1)
  
    def benchmarkAdjustHueCpuAll(self):
      self._benchmarkAdjustHue("/cpu:0", None)
  
    def benchmarkAdjustHueGpu(self):
      self._benchmarkAdjustHue(test.gpu_device_name(), None)
  
  
  class AdjustSaturationBenchmark(test.Benchmark):
  
    def _benchmarkAdjustSaturation(self, device, cpu_count):
      image_shape = [299, 299, 3]
      warmup_rounds = 100
      benchmark_rounds = 1000
      config = config_pb2.ConfigProto()
      if cpu_count is not None:
        config.inter_op_parallelism_threads = 1
        config.intra_op_parallelism_threads = cpu_count
      with session.Session("", graph=ops.Graph(), config=config) as sess:
        with ops.device(device):
          inputs = variables.Variable(
              random_ops.random_uniform(image_shape, dtype=dtypes.float32) * 255,
              trainable=False,
              dtype=dtypes.float32)
          delta = constant_op.constant(0.1, dtype=dtypes.float32)
          outputs = image_ops.adjust_saturation(inputs, delta)
          run_op = control_flow_ops.group(outputs)
          sess.run(variables.global_variables_initializer())
          for _ in xrange(warmup_rounds):
            sess.run(run_op)
          start = time.time()
          for _ in xrange(benchmark_rounds):
            sess.run(run_op)
      end = time.time()
      step_time = (end - start) / benchmark_rounds
      tag = device + "_%s" % (cpu_count if cpu_count is not None else "_all")
      print("benchmarkAdjustSaturation_299_299_3_%s step_time: %.2f us" %
            (tag, step_time * 1e6))
      self.report_benchmark(
          name="benchmarkAdjustSaturation_299_299_3_%s" % (tag),
          iters=benchmark_rounds,
          wall_time=step_time)
  
    def benchmarkAdjustSaturationCpu1(self):
      self._benchmarkAdjustSaturation("/cpu:0", 1)
  
    def benchmarkAdjustSaturationCpuAll(self):
      self._benchmarkAdjustSaturation("/cpu:0", None)
  
    def benchmarkAdjustSaturationGpu(self):
      self._benchmarkAdjustSaturation(test.gpu_device_name(), None)
  
  
  class ResizeBilinearBenchmark(test.Benchmark):
  
    def _benchmarkResize(self, image_size, num_channels):
      batch_size = 1
      num_ops = 1000
      img = variables.Variable(
          random_ops.random_normal(
              [batch_size, image_size[0], image_size[1], num_channels]),
          name="img")
  
      deps = []
      for _ in xrange(num_ops):
        with ops.control_dependencies(deps):
          resize_op = image_ops.resize_bilinear(
              img, [299, 299], align_corners=False)
          deps = [resize_op]
        benchmark_op = control_flow_ops.group(*deps)
  
      with session.Session() as sess:
        sess.run(variables.global_variables_initializer())
        results = self.run_op_benchmark(
            sess,
            benchmark_op,
            name=("resize_bilinear_%s_%s_%s" % (image_size[0], image_size[1],
                                                num_channels)))
        print("%s   : %.2f ms/img" %
              (results["name"],
               1000 * results["wall_time"] / (batch_size * num_ops)))
  
    def benchmarkSimilar3Channel(self):
      self._benchmarkResize((183, 229), 3)
  
    def benchmarkScaleUp3Channel(self):
      self._benchmarkResize((141, 186), 3)
  
    def benchmarkScaleDown3Channel(self):
      self._benchmarkResize((749, 603), 3)
  
    def benchmarkSimilar1Channel(self):
      self._benchmarkResize((183, 229), 1)
  
    def benchmarkScaleUp1Channel(self):
      self._benchmarkResize((141, 186), 1)
  
    def benchmarkScaleDown1Channel(self):
      self._benchmarkResize((749, 603), 1)
  
  
  class ResizeBicubicBenchmark(test.Benchmark):
  
    def _benchmarkResize(self, image_size, num_channels):
      batch_size = 1
      num_ops = 1000
      img = variables.Variable(
          random_ops.random_normal(
              [batch_size, image_size[0], image_size[1], num_channels]),
          name="img")
  
      deps = []
      for _ in xrange(num_ops):
        with ops.control_dependencies(deps):
          resize_op = image_ops.resize_bicubic(
              img, [299, 299], align_corners=False)
          deps = [resize_op]
        benchmark_op = control_flow_ops.group(*deps)
  
      with session.Session() as sess:
        sess.run(variables.global_variables_initializer())
        results = self.run_op_benchmark(
            sess,
            benchmark_op,
            min_iters=20,
            name=("resize_bicubic_%s_%s_%s" % (image_size[0], image_size[1],
                                               num_channels)))
        print("%s   : %.2f ms/img" %
              (results["name"],
               1000 * results["wall_time"] / (batch_size * num_ops)))
  
    def benchmarkSimilar3Channel(self):
      self._benchmarkResize((183, 229), 3)
  
    def benchmarkScaleUp3Channel(self):
      self._benchmarkResize((141, 186), 3)
  
    def benchmarkScaleDown3Channel(self):
      self._benchmarkResize((749, 603), 3)
  
    def benchmarkSimilar1Channel(self):
      self._benchmarkResize((183, 229), 1)
  
    def benchmarkScaleUp1Channel(self):
      self._benchmarkResize((141, 186), 1)
  
    def benchmarkScaleDown1Channel(self):
      self._benchmarkResize((749, 603), 1)
  
    def benchmarkSimilar4Channel(self):
      self._benchmarkResize((183, 229), 4)
  
    def benchmarkScaleUp4Channel(self):
      self._benchmarkResize((141, 186), 4)
  
    def benchmarkScaleDown4Channel(self):
      self._benchmarkResize((749, 603), 4)
  
  
  class ResizeAreaBenchmark(test.Benchmark):
  
    def _benchmarkResize(self, image_size, num_channels):
      batch_size = 1
      num_ops = 1000
      img = variables.Variable(
          random_ops.random_normal(
              [batch_size, image_size[0], image_size[1], num_channels]),
          name="img")
  
      deps = []
      for _ in xrange(num_ops):
        with ops.control_dependencies(deps):
          resize_op = image_ops.resize_area(img, [299, 299], align_corners=False)
          deps = [resize_op]
        benchmark_op = control_flow_ops.group(*deps)
  
      with session.Session() as sess:
        sess.run(variables.global_variables_initializer())
        results = self.run_op_benchmark(
            sess,
            benchmark_op,
            name=("resize_area_%s_%s_%s" % (image_size[0], image_size[1],
                                            num_channels)))
        print("%s   : %.2f ms/img" %
              (results["name"],
               1000 * results["wall_time"] / (batch_size * num_ops)))
  
    def benchmarkSimilar3Channel(self):
      self._benchmarkResize((183, 229), 3)
  
    def benchmarkScaleUp3Channel(self):
      self._benchmarkResize((141, 186), 3)
  
    def benchmarkScaleDown3Channel(self):
      self._benchmarkResize((749, 603), 3)
  
    def benchmarkSimilar1Channel(self):
      self._benchmarkResize((183, 229), 1)
  
    def benchmarkScaleUp1Channel(self):
      self._benchmarkResize((141, 186), 1)
  
    def benchmarkScaleDown1Channel(self):
      self._benchmarkResize((749, 603), 1)
  
  
  class AdjustSaturationTest(test_util.TensorFlowTestCase):
  
    def testHalfSaturation(self):
      x_shape = [2, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
  
      saturation_factor = 0.5
      y_data = [6, 9, 13, 140, 180, 226, 135, 121, 234, 172, 255, 128]
      y_np = np.array(y_data, dtype=np.uint8).reshape(x_shape)
  
      with self.test_session(use_gpu=True):
        x = constant_op.constant(x_np, shape=x_shape)
        y = image_ops.adjust_saturation(x, saturation_factor)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def testTwiceSaturation(self):
      x_shape = [2, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
  
      saturation_factor = 2.0
      y_data = [0, 5, 13, 0, 106, 226, 30, 0, 234, 89, 255, 0]
      y_np = np.array(y_data, dtype=np.uint8).reshape(x_shape)
  
      with self.test_session(use_gpu=True):
        x = constant_op.constant(x_np, shape=x_shape)
        y = image_ops.adjust_saturation(x, saturation_factor)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def testBatchSaturation(self):
      x_shape = [2, 1, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
  
      saturation_factor = 0.5
      y_data = [6, 9, 13, 140, 180, 226, 135, 121, 234, 172, 255, 128]
      y_np = np.array(y_data, dtype=np.uint8).reshape(x_shape)
  
      with self.test_session(use_gpu=True):
        x = constant_op.constant(x_np, shape=x_shape)
        y = image_ops.adjust_saturation(x, saturation_factor)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def _adjust_saturation(self, image, saturation_factor):
      image = ops.convert_to_tensor(image, name="image")
      orig_dtype = image.dtype
      flt_image = image_ops.convert_image_dtype(image, dtypes.float32)
      saturation_adjusted_image = gen_image_ops.adjust_saturation(
          flt_image, saturation_factor)
      return image_ops.convert_image_dtype(saturation_adjusted_image, orig_dtype)
  
    def testHalfSaturationFused(self):
      x_shape = [2, 2, 3]
      x_rgb_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_rgb_data, dtype=np.uint8).reshape(x_shape)
  
      saturation_factor = 0.5
      y_rgb_data = [6, 9, 13, 140, 180, 226, 135, 121, 234, 172, 255, 128]
      y_np = np.array(y_rgb_data, dtype=np.uint8).reshape(x_shape)
  
      with self.test_session(use_gpu=True):
        x = constant_op.constant(x_np, shape=x_shape)
        y = self._adjust_saturation(x, saturation_factor)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def testTwiceSaturationFused(self):
      x_shape = [2, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
  
      saturation_factor = 2.0
      y_data = [0, 5, 13, 0, 106, 226, 30, 0, 234, 89, 255, 0]
      y_np = np.array(y_data, dtype=np.uint8).reshape(x_shape)
  
      with self.test_session(use_gpu=True):
        x = constant_op.constant(x_np, shape=x_shape)
        y = self._adjust_saturation(x, saturation_factor)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def _adjustSaturationNp(self, x_np, scale):
      self.assertEqual(x_np.shape[-1], 3)
      x_v = x_np.reshape([-1, 3])
      y_v = np.ndarray(x_v.shape, dtype=x_v.dtype)
      channel_count = x_v.shape[0]
      for i in xrange(channel_count):
        r = x_v[i][0]
        g = x_v[i][1]
        b = x_v[i][2]
        h, s, v = colorsys.rgb_to_hsv(r, g, b)
        s *= scale
        s = min(1.0, max(0.0, s))
        r, g, b = colorsys.hsv_to_rgb(h, s, v)
        y_v[i][0] = r
        y_v[i][1] = g
        y_v[i][2] = b
      return y_v.reshape(x_np.shape)
  
    def testAdjustRandomSaturation(self):
      x_shapes = [
          [2, 2, 3],
          [4, 2, 3],
          [2, 4, 3],
          [2, 5, 3],
          [1000, 1, 3],
      ]
      test_styles = [
          "all_random",
          "rg_same",
          "rb_same",
          "gb_same",
          "rgb_same",
      ]
      with self.test_session(use_gpu=True):
        for x_shape in x_shapes:
          for test_style in test_styles:
            x_np = np.random.rand(*x_shape) * 255.
            scale = np.random.rand()
            if test_style == "all_random":
              pass
            elif test_style == "rg_same":
              x_np[..., 1] = x_np[..., 0]
            elif test_style == "rb_same":
              x_np[..., 2] = x_np[..., 0]
            elif test_style == "gb_same":
              x_np[..., 2] = x_np[..., 1]
            elif test_style == "rgb_same":
              x_np[..., 1] = x_np[..., 0]
              x_np[..., 2] = x_np[..., 0]
            else:
              raise AssertionError("Invalid test style: %s" % (test_style))
            y_baseline = self._adjustSaturationNp(x_np, scale)
            y_fused = self._adjust_saturation(x_np, scale).eval()
            self.assertAllClose(y_fused, y_baseline, rtol=2e-5, atol=1e-5)
  
  
  class FlipTransposeRotateTest(test_util.TensorFlowTestCase):
  
    def testInvolutionLeftRight(self):
      x_np = np.array([[1, 2, 3], [1, 2, 3]], dtype=np.uint8).reshape([2, 3, 1])
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.flip_left_right(image_ops.flip_left_right(x_tf))
        y_tf = y.eval()
        self.assertAllEqual(y_tf, x_np)
  
    def testInvolutionLeftRightWithBatch(self):
      x_np = np.array(
          [[[1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3]]],
          dtype=np.uint8).reshape([2, 2, 3, 1])
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.flip_left_right(image_ops.flip_left_right(x_tf))
        y_tf = y.eval()
        self.assertAllEqual(y_tf, x_np)
  
    def testLeftRight(self):
      x_np = np.array([[1, 2, 3], [1, 2, 3]], dtype=np.uint8).reshape([2, 3, 1])
      y_np = np.array([[3, 2, 1], [3, 2, 1]], dtype=np.uint8).reshape([2, 3, 1])
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.flip_left_right(x_tf)
        self.assertTrue(y.op.name.startswith("flip_left_right"))
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def testLeftRightWithBatch(self):
      x_np = np.array(
          [[[1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3]]],
          dtype=np.uint8).reshape([2, 2, 3, 1])
      y_np = np.array(
          [[[3, 2, 1], [3, 2, 1]], [[3, 2, 1], [3, 2, 1]]],
          dtype=np.uint8).reshape([2, 2, 3, 1])
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.flip_left_right(x_tf)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def testRandomFlipLeftRight(self):
      x_np = np.array([[1, 2, 3], [1, 2, 3]], dtype=np.uint8).reshape([2, 3, 1])
      y_np = np.array([[3, 2, 1], [3, 2, 1]], dtype=np.uint8).reshape([2, 3, 1])
      seed = 42
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.random_flip_left_right(x_tf, seed=seed)
        self.assertTrue(y.op.name.startswith("random_flip_left_right"))
  
        count_flipped = 0
        count_unflipped = 0
        for _ in range(100):
          y_tf = y.eval()
          if y_tf[0][0] == 1:
            self.assertAllEqual(y_tf, x_np)
            count_unflipped += 1
          else:
            self.assertAllEqual(y_tf, y_np)
            count_flipped += 1
  
        # 100 trials
        # Mean: 50
        # Std Dev: ~5
        # Six Sigma: 50 - (5 * 6) = 20
        self.assertGreaterEqual(count_flipped, 20)
        self.assertGreaterEqual(count_unflipped, 20)
  
    def testRandomFlipLeftRightWithBatch(self):
      batch_size = 16
      seed = 42
  
      # create single item of test data
      x_np_raw = np.array(
          [[1, 2, 3], [1, 2, 3]], dtype=np.uint8
      ).reshape([1, 2, 3, 1])
      y_np_raw = np.array(
          [[3, 2, 1], [3, 2, 1]], dtype=np.uint8
      ).reshape([1, 2, 3, 1])
  
      # create batched test data
      x_np = np.vstack([x_np_raw for _ in range(batch_size)])
      y_np = np.vstack([y_np_raw for _ in range(batch_size)])
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.random_flip_left_right(x_tf, seed=seed)
        self.assertTrue(y.op.name.startswith("random_flip_left_right"))
  
        count_flipped = 0
        count_unflipped = 0
        for _ in range(100):
          y_tf = y.eval()
  
          # check every element of the batch
          for i in range(batch_size):
            if y_tf[i][0][0] == 1:
              self.assertAllEqual(y_tf[i], x_np[i])
              count_unflipped += 1
            else:
              self.assertAllEqual(y_tf[i], y_np[i])
              count_flipped += 1
  
        # 100 trials, each containing batch_size elements
        # Mean: 50 * batch_size
        # Std Dev: ~5 * sqrt(batch_size)
        # Six Sigma: 50 * batch_size - (5 * 6 * sqrt(batch_size))
        #          = 50 * batch_size - 30 * sqrt(batch_size) = 800 - 30 * 4 = 680
        six_sigma = 50 * batch_size - 30 * np.sqrt(batch_size)
        self.assertGreaterEqual(count_flipped, six_sigma)
        self.assertGreaterEqual(count_unflipped, six_sigma)
  
    def testInvolutionUpDown(self):
      x_np = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.uint8).reshape([2, 3, 1])
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.flip_up_down(image_ops.flip_up_down(x_tf))
        y_tf = y.eval()
        self.assertAllEqual(y_tf, x_np)
  
    def testInvolutionUpDownWithBatch(self):
      x_np = np.array(
          [[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]],
          dtype=np.uint8).reshape([2, 2, 3, 1])
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.flip_up_down(image_ops.flip_up_down(x_tf))
        y_tf = y.eval()
        self.assertAllEqual(y_tf, x_np)
  
    def testUpDown(self):
      x_np = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.uint8).reshape([2, 3, 1])
      y_np = np.array([[4, 5, 6], [1, 2, 3]], dtype=np.uint8).reshape([2, 3, 1])
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.flip_up_down(x_tf)
        self.assertTrue(y.op.name.startswith("flip_up_down"))
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def testUpDownWithBatch(self):
      x_np = np.array(
          [[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]],
          dtype=np.uint8).reshape([2, 2, 3, 1])
      y_np = np.array(
          [[[4, 5, 6], [1, 2, 3]], [[10, 11, 12], [7, 8, 9]]],
          dtype=np.uint8).reshape([2, 2, 3, 1])
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.flip_up_down(x_tf)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def testRandomFlipUpDown(self):
      x_np = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.uint8).reshape([2, 3, 1])
      y_np = np.array([[4, 5, 6], [1, 2, 3]], dtype=np.uint8).reshape([2, 3, 1])
  
      seed = 42
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.random_flip_up_down(x_tf, seed=seed)
        self.assertTrue(y.op.name.startswith("random_flip_up_down"))
        count_flipped = 0
        count_unflipped = 0
        for _ in range(100):
          y_tf = y.eval()
          if y_tf[0][0] == 1:
            self.assertAllEqual(y_tf, x_np)
            count_unflipped += 1
          else:
            self.assertAllEqual(y_tf, y_np)
            count_flipped += 1
  
        # 100 trials
        # Mean: 50
        # Std Dev: ~5
        # Six Sigma: 50 - (5 * 6) = 20
        self.assertGreaterEqual(count_flipped, 20)
        self.assertGreaterEqual(count_unflipped, 20)
  
    def testRandomFlipUpDownWithBatch(self):
      batch_size = 16
      seed = 42
  
      # create single item of test data
      x_np_raw = np.array(
          [[1, 2, 3], [4, 5, 6]], dtype=np.uint8
      ).reshape([1, 2, 3, 1])
      y_np_raw = np.array(
          [[4, 5, 6], [1, 2, 3]], dtype=np.uint8
      ).reshape([1, 2, 3, 1])
  
      # create batched test data
      x_np = np.vstack([x_np_raw for _ in range(batch_size)])
      y_np = np.vstack([y_np_raw for _ in range(batch_size)])
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.random_flip_up_down(x_tf, seed=seed)
        self.assertTrue(y.op.name.startswith("random_flip_up_down"))
  
        count_flipped = 0
        count_unflipped = 0
        for _ in range(100):
          y_tf = y.eval()
  
          # check every element of the batch
          for i in range(batch_size):
            if y_tf[i][0][0] == 1:
              self.assertAllEqual(y_tf[i], x_np[i])
              count_unflipped += 1
            else:
              self.assertAllEqual(y_tf[i], y_np[i])
              count_flipped += 1
  
        # 100 trials, each containing batch_size elements
        # Mean: 50 * batch_size
        # Std Dev: ~5 * sqrt(batch_size)
        # Six Sigma: 50 * batch_size - (5 * 6 * sqrt(batch_size))
        #          = 50 * batch_size - 30 * sqrt(batch_size) = 800 - 30 * 4 = 680
        six_sigma = 50 * batch_size - 30 * np.sqrt(batch_size)
        self.assertGreaterEqual(count_flipped, six_sigma)
        self.assertGreaterEqual(count_unflipped, six_sigma)
  
    def testInvolutionTranspose(self):
      x_np = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.uint8).reshape([2, 3, 1])
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.transpose_image(image_ops.transpose_image(x_tf))
        y_tf = y.eval()
        self.assertAllEqual(y_tf, x_np)
  
    def testInvolutionTransposeWithBatch(self):
      x_np = np.array(
          [[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]],
          dtype=np.uint8).reshape([2, 2, 3, 1])
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.transpose_image(image_ops.transpose_image(x_tf))
        y_tf = y.eval()
        self.assertAllEqual(y_tf, x_np)
  
    def testTranspose(self):
      x_np = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.uint8).reshape([2, 3, 1])
      y_np = np.array([[1, 4], [2, 5], [3, 6]], dtype=np.uint8).reshape([3, 2, 1])
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.transpose_image(x_tf)
        self.assertTrue(y.op.name.startswith("transpose_image"))
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def testTransposeWithBatch(self):
      x_np = np.array(
          [[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]],
          dtype=np.uint8).reshape([2, 2, 3, 1])
  
      y_np = np.array(
          [[[1, 4], [2, 5], [3, 6]], [[7, 10], [8, 11], [9, 12]]],
          dtype=np.uint8).reshape([2, 3, 2, 1])
  
      with self.test_session(use_gpu=True):
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.transpose_image(x_tf)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
  
    def testPartialShapes(self):
      p_unknown_rank = array_ops.placeholder(dtypes.uint8)
      p_unknown_dims_3 = array_ops.placeholder(
          dtypes.uint8, shape=[None, None, None])
      p_unknown_dims_4 = array_ops.placeholder(
          dtypes.uint8, shape=[None, None, None, None])
      p_unknown_width = array_ops.placeholder(dtypes.uint8, shape=[64, None, 3])
      p_unknown_batch = array_ops.placeholder(
          dtypes.uint8, shape=[None, 64, 64, 3])
      p_wrong_rank = array_ops.placeholder(dtypes.uint8, shape=[None, None])
      p_zero_dim = array_ops.placeholder(dtypes.uint8, shape=[64, 0, 3])
  
      #Ops that support 3D input
      for op in [
          image_ops.flip_left_right, image_ops.flip_up_down,
          image_ops.random_flip_left_right, image_ops.random_flip_up_down,
          image_ops.transpose_image, image_ops.rot90
      ]:
        transformed_unknown_rank = op(p_unknown_rank)
        self.assertEqual(3, transformed_unknown_rank.get_shape().ndims)
        transformed_unknown_dims_3 = op(p_unknown_dims_3)
        self.assertEqual(3, transformed_unknown_dims_3.get_shape().ndims)
        transformed_unknown_width = op(p_unknown_width)
        self.assertEqual(3, transformed_unknown_width.get_shape().ndims)
  
        with self.assertRaisesRegexp(ValueError, "must be > 0"):
          op(p_zero_dim)
  
      #Ops that support 4D input
      for op in [
          image_ops.flip_left_right, image_ops.flip_up_down,
          image_ops.random_flip_left_right, image_ops.random_flip_up_down,
          image_ops.transpose_image, image_ops.rot90
      ]:
        transformed_unknown_dims_4 = op(p_unknown_dims_4)
        self.assertEqual(4, transformed_unknown_dims_4.get_shape().ndims)
        transformed_unknown_batch = op(p_unknown_batch)
        self.assertEqual(4, transformed_unknown_batch.get_shape().ndims)
        with self.assertRaisesRegexp(ValueError,
                                     "must be at least three-dimensional"):
          op(p_wrong_rank)
  
    def testRot90GroupOrder(self):
      image = np.arange(24, dtype=np.uint8).reshape([2, 4, 3])
      with self.test_session(use_gpu=True):
        rotated = image
        for _ in xrange(4):
          rotated = image_ops.rot90(rotated)
        self.assertAllEqual(image, rotated.eval())
  
    def testRot90GroupOrderWithBatch(self):
      image = np.arange(48, dtype=np.uint8).reshape([2, 2, 4, 3])
      with self.test_session(use_gpu=True):
        rotated = image
        for _ in xrange(4):
          rotated = image_ops.rot90(rotated)
        self.assertAllEqual(image, rotated.eval())
  
    def testRot90NumpyEquivalence(self):
      image = np.arange(24, dtype=np.uint8).reshape([2, 4, 3])
      with self.test_session(use_gpu=True):
        k_placeholder = array_ops.placeholder(dtypes.int32, shape=[])
        y_tf = image_ops.rot90(image, k_placeholder)
        for k in xrange(4):
          y_np = np.rot90(image, k=k)
          self.assertAllEqual(y_np, y_tf.eval({k_placeholder: k}))
  
    def testRot90NumpyEquivalenceWithBatch(self):
      image = np.arange(48, dtype=np.uint8).reshape([2, 2, 4, 3])
      with self.test_session(use_gpu=True):
        k_placeholder = array_ops.placeholder(dtypes.int32, shape=[])
        y_tf = image_ops.rot90(image, k_placeholder)
        for k in xrange(4):
          y_np = np.rot90(image, k=k, axes=(1, 2))
          self.assertAllEqual(y_np, y_tf.eval({k_placeholder: k}))
  
  class AdjustContrastTest(test_util.TensorFlowTestCase):
  
    def _testContrast(self, x_np, y_np, contrast_factor):
      with self.test_session(use_gpu=True):
        x = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.adjust_contrast(x, contrast_factor)
        y_tf = y.eval()
        self.assertAllClose(y_tf, y_np, 1e-6)
  
    def testDoubleContrastUint8(self):
      x_shape = [1, 2, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
  
      y_data = [0, 0, 0, 62, 169, 255, 28, 0, 255, 135, 255, 0]
      y_np = np.array(y_data, dtype=np.uint8).reshape(x_shape)
  
      self._testContrast(x_np, y_np, contrast_factor=2.0)
  
    def testDoubleContrastFloat(self):
      x_shape = [1, 2, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.float).reshape(x_shape) / 255.
  
      y_data = [
          -45.25, -90.75, -92.5, 62.75, 169.25, 333.5, 28.75, -84.75, 349.5,
          134.75, 409.25, -116.5
      ]
      y_np = np.array(y_data, dtype=np.float).reshape(x_shape) / 255.
  
      self._testContrast(x_np, y_np, contrast_factor=2.0)
  
    def testHalfContrastUint8(self):
      x_shape = [1, 2, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
  
      y_data = [22, 52, 65, 49, 118, 172, 41, 54, 176, 67, 178, 59]
      y_np = np.array(y_data, dtype=np.uint8).reshape(x_shape)
  
      self._testContrast(x_np, y_np, contrast_factor=0.5)
  
    def testBatchDoubleContrast(self):
      x_shape = [2, 1, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
  
      y_data = [0, 0, 0, 81, 200, 255, 10, 0, 255, 116, 255, 0]
      y_np = np.array(y_data, dtype=np.uint8).reshape(x_shape)
  
      self._testContrast(x_np, y_np, contrast_factor=2.0)
  
    def _adjustContrastNp(self, x_np, contrast_factor):
      mean = np.mean(x_np, (1, 2), keepdims=True)
      y_np = mean + contrast_factor * (x_np - mean)
      return y_np
  
    def _adjustContrastTf(self, x_np, contrast_factor):
      with self.test_session(use_gpu=True):
        x = constant_op.constant(x_np)
        y = image_ops.adjust_contrast(x, contrast_factor)
        y_tf = y.eval()
      return y_tf
  
    def testRandomContrast(self):
      x_shapes = [
          [1, 2, 2, 3],
          [2, 1, 2, 3],
          [1, 2, 2, 3],
          [2, 5, 5, 3],
          [2, 1, 1, 3],
      ]
      for x_shape in x_shapes:
        x_np = np.random.rand(*x_shape) * 255.
        contrast_factor = np.random.rand() * 2.0 + 0.1
        y_np = self._adjustContrastNp(x_np, contrast_factor)
        y_tf = self._adjustContrastTf(x_np, contrast_factor)
        self.assertAllClose(y_tf, y_np, rtol=1e-5, atol=1e-5)
  
    def testContrastFactorShape(self):
      x_shape = [1, 2, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
      with self.assertRaisesRegexp(
          ValueError, 'Shape must be rank 0 but is rank 1'):
        image_ops.adjust_contrast(x_np, [2.0])
  
  
  class AdjustBrightnessTest(test_util.TensorFlowTestCase):
  
    def _testBrightness(self, x_np, y_np, delta):
      with self.test_session(use_gpu=True):
        x = constant_op.constant(x_np, shape=x_np.shape)
        y = image_ops.adjust_brightness(x, delta)
        y_tf = y.eval()
        self.assertAllClose(y_tf, y_np, 1e-6)
  
    def testPositiveDeltaUint8(self):
      x_shape = [2, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
  
      y_data = [10, 15, 23, 64, 145, 236, 47, 18, 244, 100, 255, 11]
      y_np = np.array(y_data, dtype=np.uint8).reshape(x_shape)
  
      self._testBrightness(x_np, y_np, delta=10. / 255.)
  
    def testPositiveDeltaFloat(self):
      x_shape = [2, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.float32).reshape(x_shape) / 255.
  
      y_data = [10, 15, 23, 64, 145, 236, 47, 18, 244, 100, 265, 11]
      y_np = np.array(y_data, dtype=np.float32).reshape(x_shape) / 255.
  
      self._testBrightness(x_np, y_np, delta=10. / 255.)
  
    def testNegativeDelta(self):
      x_shape = [2, 2, 3]
      x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
      x_np = np.array(x_data, dtype=np.uint8).reshape(x_shape)
  
      y_data = [0, 0, 3, 44, 125, 216, 27, 0, 224, 80, 245, 0]
      y_np = np.array(y_data, dtype=np.uint8).reshape(x_shape)
  
      self._testBrightness(x_np, y_np, delta=-10. / 255.)
  
  
  class PerImageWhiteningTest(test_util.TensorFlowTestCase):
  
    def _NumpyPerImageWhitening(self, x):
      num_pixels = np.prod(x.shape)
      x2 = np.square(x).astype(np.float32)
      mn = np.mean(x)
      vr = np.mean(x2) - (mn * mn)
      stddev = max(math.sqrt(vr), 1.0 / math.sqrt(num_pixels))
  
      y = x.astype(np.float32)
      y -= mn
      y /= stddev
      return y
  
    def testBasic(self):
      x_shape = [13, 9, 3]
      x_np = np.arange(0, np.prod(x_shape), dtype=np.int32).reshape(x_shape)
      y_np = self._NumpyPerImageWhitening(x_np)
  
      with self.test_session(use_gpu=True):
        x = constant_op.constant(x_np, shape=x_shape)
        y = image_ops.per_image_standardization(x)
        self.assertTrue(y.op.name.startswith("per_image_standardization"))
        y_tf = y.eval()
        self.assertAllClose(y_tf, y_np, atol=1e-4)
  
    def testUniformImage(self):
      im_np = np.ones([19, 19, 3]).astype(np.float32) * 249
      im = constant_op.constant(im_np)
      whiten = image_ops.per_image_standardization(im)
      with self.test_session(use_gpu=True):
        whiten_np = whiten.eval()
        self.assertFalse(np.any(np.isnan(whiten_np)))
  
  
  class CropToBoundingBoxTest(test_util.TensorFlowTestCase):
  
    def _CropToBoundingBox(self, x, offset_height, offset_width, target_height,
                           target_width, use_tensor_inputs):
      if use_tensor_inputs:
        offset_height = ops.convert_to_tensor(offset_height)
        offset_width = ops.convert_to_tensor(offset_width)
        target_height = ops.convert_to_tensor(target_height)
        target_width = ops.convert_to_tensor(target_width)
        x_tensor = array_ops.placeholder(x.dtype, shape=[None] * x.ndim)
        feed_dict = {x_tensor: x}
      else:
        x_tensor = x
        feed_dict = {}
  
      y = image_ops.crop_to_bounding_box(x_tensor, offset_height, offset_width,
                                         target_height, target_width)
      if not use_tensor_inputs:
        self.assertTrue(y.get_shape().is_fully_defined())
  
      with self.test_session(use_gpu=True):
        return y.eval(feed_dict=feed_dict)
  
    def _assertReturns(self,
                       x,
                       x_shape,
                       offset_height,
                       offset_width,
                       y,
                       y_shape,
                       use_tensor_inputs_options=None):
      use_tensor_inputs_options = use_tensor_inputs_options or [False, True]
      target_height, target_width, _ = y_shape
      x = np.array(x).reshape(x_shape)
      y = np.array(y).reshape(y_shape)
  
      for use_tensor_inputs in use_tensor_inputs_options:
        y_tf = self._CropToBoundingBox(x, offset_height, offset_width,
                                       target_height, target_width,
                                       use_tensor_inputs)
        self.assertAllClose(y, y_tf)
  
    def _assertRaises(self,
                      x,
                      x_shape,
                      offset_height,
                      offset_width,
                      target_height,
                      target_width,
                      err_msg,
                      use_tensor_inputs_options=None):
      use_tensor_inputs_options = use_tensor_inputs_options or [False, True]
      x = np.array(x).reshape(x_shape)
  
      for use_tensor_inputs in use_tensor_inputs_options:
        try:
          self._CropToBoundingBox(x, offset_height, offset_width, target_height,
                                  target_width, use_tensor_inputs)
        except Exception as e:
          if err_msg not in str(e):
            raise
        else:
          raise AssertionError("Exception not raised: %s" % err_msg)
  
    def _assertShapeInference(self, pre_shape, height, width, post_shape):
      image = array_ops.placeholder(dtypes.float32, shape=pre_shape)
      y = image_ops.crop_to_bounding_box(image, 0, 0, height, width)
      self.assertEqual(y.get_shape().as_list(), post_shape)
  
    def testNoOp(self):
      x_shape = [10, 10, 10]
      x = np.random.uniform(size=x_shape)
      self._assertReturns(x, x_shape, 0, 0, x, x_shape)
  
    def testCrop(self):
      x = [1, 2, 3, 4, 5, 6, 7, 8, 9]
      x_shape = [3, 3, 1]
  
      offset_height, offset_width = [1, 0]
      y_shape = [2, 3, 1]
      y = [4, 5, 6, 7, 8, 9]
      self._assertReturns(x, x_shape, offset_height, offset_width, y, y_shape)
  
      offset_height, offset_width = [0, 1]
      y_shape = [3, 2, 1]
      y = [2, 3, 5, 6, 8, 9]
      self._assertReturns(x, x_shape, offset_height, offset_width, y, y_shape)
  
      offset_height, offset_width = [0, 0]
      y_shape = [2, 3, 1]
      y = [1, 2, 3, 4, 5, 6]
      self._assertReturns(x, x_shape, offset_height, offset_width, y, y_shape)
  
      offset_height, offset_width = [0, 0]
      y_shape = [3, 2, 1]
      y = [1, 2, 4, 5, 7, 8]
      self._assertReturns(x, x_shape, offset_height, offset_width, y, y_shape)
  
    def testShapeInference(self):
      self._assertShapeInference([55, 66, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([59, 69, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([None, 66, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([None, 69, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([55, None, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([59, None, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([None, None, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([55, 66, None], 55, 66, [55, 66, None])
      self._assertShapeInference([59, 69, None], 55, 66, [55, 66, None])
      self._assertShapeInference([None, None, None], 55, 66, [55, 66, None])
      self._assertShapeInference(None, 55, 66, [55, 66, None])
  
    def testNon3DInput(self):
      # Input image is not 3D
      x = [0] * 15
      offset_height, offset_width = [0, 0]
      target_height, target_width = [2, 2]
  
      for x_shape in ([3, 5], [1, 3, 5, 1, 1]):
        self._assertRaises(x, x_shape, offset_height, offset_width, target_height,
                           target_width,
                           "'image' must have either 3 or 4 dimensions.")
  
    def testZeroLengthInput(self):
      # Input image has 0-length dimension(s).
      # Each line is a test configuration:
      #   x_shape, target_height, target_width
      test_config = (([0, 2, 2], 1, 1), ([2, 0, 2], 1, 1), ([2, 2, 0], 1, 1),
                     ([0, 2, 2], 0, 1), ([2, 0, 2], 1, 0))
      offset_height, offset_width = [0, 0]
      x = []
  
      for x_shape, target_height, target_width in test_config:
        self._assertRaises(
            x,
            x_shape,
            offset_height,
            offset_width,
            target_height,
            target_width,
            "all dims of 'image.shape' must be > 0",
            use_tensor_inputs_options=[False])
        # Multiple assertion could fail, but the evaluation order is arbitrary.
        # Match gainst generic pattern.
        self._assertRaises(
            x,
            x_shape,
            offset_height,
            offset_width,
            target_height,
            target_width,
            "assertion failed:",
            use_tensor_inputs_options=[True])
  
    def testBadParams(self):
      x_shape = [4, 4, 1]
      x = np.zeros(x_shape)
  
      # Each line is a test configuration:
      #   (offset_height, offset_width, target_height, target_width), err_msg
      test_config = (([-1, 0, 3, 3], "offset_height must be >= 0"), ([
          0, -1, 3, 3
      ], "offset_width must be >= 0"), ([0, 0, 0, 3],
                                        "target_height must be > 0"),
                     ([0, 0, 3, 0], "target_width must be > 0"),
                     ([2, 0, 3, 3], "height must be >= target + offset"),
                     ([0, 2, 3, 3], "width must be >= target + offset"))
  
      for params, err_msg in test_config:
        self._assertRaises(x, x_shape, *params, err_msg=err_msg)
  
    def testNameScope(self):
      image = array_ops.placeholder(dtypes.float32, shape=[55, 66, 3])
      y = image_ops.crop_to_bounding_box(image, 0, 0, 55, 66)
      self.assertTrue(y.name.startswith("crop_to_bounding_box"))
  
  
  class CentralCropTest(test_util.TensorFlowTestCase):
  
    def _assertShapeInference(self, pre_shape, fraction, post_shape):
      image = array_ops.placeholder(dtypes.float32, shape=pre_shape)
      y = image_ops.central_crop(image, fraction)
      if post_shape is None:
        self.assertEqual(y.get_shape().dims, None)
      else:
        self.assertEqual(y.get_shape().as_list(), post_shape)
  
    def testNoOp(self):
      x_shapes = [[13, 9, 3], [5, 13, 9, 3]]
      for x_shape in x_shapes:
        x_np = np.ones(x_shape, dtype=np.float32)
        for use_gpu in [True, False]:
          with self.test_session(use_gpu=use_gpu):
            x = constant_op.constant(x_np, shape=x_shape)
            y = image_ops.central_crop(x, 1.0)
            y_tf = y.eval()
            self.assertAllEqual(y_tf, x_np)
            self.assertEqual(y.op.name, x.op.name)
  
    def testCropping(self):
      x_shape = [4, 8, 1]
      x_np = np.array(
          [[1, 2, 3, 4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7, 8],
           [1, 2, 3, 4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7, 8]],
          dtype=np.int32).reshape(x_shape)
      y_np = np.array([[3, 4, 5, 6], [3, 4, 5, 6]]).reshape([2, 4, 1])
      for use_gpu in [True, False]:
        with self.test_session(use_gpu=use_gpu):
          x = constant_op.constant(x_np, shape=x_shape)
          y = image_ops.central_crop(x, 0.5)
          y_tf = y.eval()
          self.assertAllEqual(y_tf, y_np)
          self.assertAllEqual(y_tf.shape, y_np.shape)
  
      x_shape = [2, 4, 8, 1]
      x_np = np.array(
          [[1, 2, 3, 4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7, 8],
           [1, 2, 3, 4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7, 8],
           [8, 7, 6, 5, 4, 3, 2, 1], [8, 7, 6, 5, 4, 3, 2, 1],
           [8, 7, 6, 5, 4, 3, 2, 1], [8, 7, 6, 5, 4, 3, 2, 1]],
          dtype=np.int32).reshape(x_shape)
      y_np = np.array([[[3, 4, 5, 6], [3, 4, 5, 6]],
                       [[6, 5, 4, 3], [6, 5, 4, 3]]]).reshape([2, 2, 4, 1])
      with self.test_session(use_gpu=True):
        x = constant_op.constant(x_np, shape=x_shape)
        y = image_ops.central_crop(x, 0.5)
        y_tf = y.eval()
        self.assertAllEqual(y_tf, y_np)
        self.assertAllEqual(y_tf.shape, y_np.shape)
  
    def testCropping2(self):
      # Test case for 10315
      x_shapes = [[240, 320, 3], [5, 240, 320, 3]]
      expected_y_shapes = [[80, 106, 3], [5, 80, 106, 3]]
  
      for x_shape, y_shape in zip(x_shapes, expected_y_shapes):
        x_np = np.zeros(x_shape, dtype=np.int32)
        y_np = np.zeros(y_shape, dtype=np.int32)
        for use_gpu in [True, False]:
          with self.test_session(use_gpu=use_gpu):
            x = array_ops.placeholder(shape=x_shape, dtype=dtypes.int32)
            y = image_ops.central_crop(x, 0.33)
            y_tf = y.eval(feed_dict={x: x_np})
            self.assertAllEqual(y_tf, y_np)
            self.assertAllEqual(y_tf.shape, y_np.shape)
  
    def testShapeInference(self):
      # Test no-op fraction=1.0, with 3-D tensors.
      self._assertShapeInference([50, 60, 3], 1.0, [50, 60, 3])
      self._assertShapeInference([None, 60, 3], 1.0, [None, 60, 3])
      self._assertShapeInference([50, None, 3], 1.0, [50, None, 3])
      self._assertShapeInference([None, None, 3], 1.0, [None, None, 3])
      self._assertShapeInference([50, 60, None], 1.0, [50, 60, None])
      self._assertShapeInference([None, None, None], 1.0, [None, None, None])
  
      # Test no-op fraction=0.5, with 3-D tensors.
      self._assertShapeInference([50, 60, 3], 0.5, [26, 30, 3])
      self._assertShapeInference([None, 60, 3], 0.5, [None, 30, 3])
      self._assertShapeInference([50, None, 3], 0.5, [26, None, 3])
      self._assertShapeInference([None, None, 3], 0.5, [None, None, 3])
      self._assertShapeInference([50, 60, None], 0.5, [26, 30, None])
      self._assertShapeInference([None, None, None], 0.5, [None, None, None])
  
      # Test no-op fraction=1.0, with 4-D tensors.
      self._assertShapeInference([5, 50, 60, 3], 1.0, [5, 50, 60, 3])
      self._assertShapeInference([5, None, 60, 3], 1.0, [5, None, 60, 3])
      self._assertShapeInference([5, 50, None, 3], 1.0, [5, 50, None, 3])
      self._assertShapeInference([5, None, None, 3], 1.0, [5, None, None, 3])
      self._assertShapeInference([5, 50, 60, None], 1.0, [5, 50, 60, None])
      self._assertShapeInference([5, None, None, None], 1.0,
                                 [5, None, None, None])
      self._assertShapeInference([None, None, None, None], 1.0,
                                 [None, None, None, None])
  
      # Test no-op fraction=0.5, with 4-D tensors.
      self._assertShapeInference([5, 50, 60, 3], 0.5, [5, 26, 30, 3])
      self._assertShapeInference([5, None, 60, 3], 0.5, [5, None, 30, 3])
      self._assertShapeInference([5, 50, None, 3], 0.5, [5, 26, None, 3])
      self._assertShapeInference([5, None, None, 3], 0.5, [5, None, None, 3])
      self._assertShapeInference([5, 50, 60, None], 0.5, [5, 26, 30, None])
      self._assertShapeInference([5, None, None, None], 0.5,
                                 [5, None, None, None])
      self._assertShapeInference([None, None, None, None], 0.5,
                                 [None, None, None, None])
  
    def testErrorOnInvalidCentralCropFractionValues(self):
      x_shape = [13, 9, 3]
      x_np = np.ones(x_shape, dtype=np.float32)
      for use_gpu in [True, False]:
        with self.test_session(use_gpu=use_gpu):
          x = constant_op.constant(x_np, shape=x_shape)
          with self.assertRaises(ValueError):
            _ = image_ops.central_crop(x, 0.0)
          with self.assertRaises(ValueError):
            _ = image_ops.central_crop(x, 1.01)
  
    def testErrorOnInvalidShapes(self):
      x_shapes = [None, [], [3], [3, 9], [3, 9, 3, 9, 3]]
      for x_shape in x_shapes:
        x_np = np.ones(x_shape, dtype=np.float32)
        for use_gpu in [True, False]:
          with self.test_session(use_gpu=use_gpu):
            x = constant_op.constant(x_np, shape=x_shape)
            with self.assertRaises(ValueError):
              _ = image_ops.central_crop(x, 0.5)
  
    def testNameScope(self):
      x_shape = [13, 9, 3]
      x_np = np.ones(x_shape, dtype=np.float32)
      for use_gpu in [True, False]:
        with self.test_session(use_gpu=use_gpu):
          y = image_ops.central_crop(x_np, 1.0)
          self.assertTrue(y.op.name.startswith("central_crop"))
  
  
  class PadToBoundingBoxTest(test_util.TensorFlowTestCase):
  
    def _PadToBoundingBox(self, x, offset_height, offset_width, target_height,
                          target_width, use_tensor_inputs):
      if use_tensor_inputs:
        offset_height = ops.convert_to_tensor(offset_height)
        offset_width = ops.convert_to_tensor(offset_width)
        target_height = ops.convert_to_tensor(target_height)
        target_width = ops.convert_to_tensor(target_width)
        x_tensor = array_ops.placeholder(x.dtype, shape=[None] * x.ndim)
        feed_dict = {x_tensor: x}
      else:
        x_tensor = x
        feed_dict = {}
  
      y = image_ops.pad_to_bounding_box(x_tensor, offset_height, offset_width,
                                        target_height, target_width)
      if not use_tensor_inputs:
        self.assertTrue(y.get_shape().is_fully_defined())
  
      with self.test_session(use_gpu=True):
        return y.eval(feed_dict=feed_dict)
  
    def _assertReturns(self,
                       x,
                       x_shape,
                       offset_height,
                       offset_width,
                       y,
                       y_shape,
                       use_tensor_inputs_options=None):
      use_tensor_inputs_options = use_tensor_inputs_options or [False, True]
      target_height, target_width, _ = y_shape
      x = np.array(x).reshape(x_shape)
      y = np.array(y).reshape(y_shape)
  
      for use_tensor_inputs in use_tensor_inputs_options:
        y_tf = self._PadToBoundingBox(x, offset_height, offset_width,
                                      target_height, target_width,
                                      use_tensor_inputs)
        self.assertAllClose(y, y_tf)
  
    def _assertRaises(self,
                      x,
                      x_shape,
                      offset_height,
                      offset_width,
                      target_height,
                      target_width,
                      err_msg,
                      use_tensor_inputs_options=None):
      use_tensor_inputs_options = use_tensor_inputs_options or [False, True]
      x = np.array(x).reshape(x_shape)
  
      for use_tensor_inputs in use_tensor_inputs_options:
        try:
          self._PadToBoundingBox(x, offset_height, offset_width, target_height,
                                 target_width, use_tensor_inputs)
        except Exception as e:
          if err_msg not in str(e):
            raise
        else:
          raise AssertionError("Exception not raised: %s" % err_msg)
  
    def _assertShapeInference(self, pre_shape, height, width, post_shape):
      image = array_ops.placeholder(dtypes.float32, shape=pre_shape)
      y = image_ops.pad_to_bounding_box(image, 0, 0, height, width)
      self.assertEqual(y.get_shape().as_list(), post_shape)
  
    def testInt64(self):
      x = [1, 2, 3, 4, 5, 6, 7, 8, 9]
      x_shape = [3, 3, 1]
  
      y = [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
      y_shape = [4, 3, 1]
      x = np.array(x).reshape(x_shape)
      y = np.array(y).reshape(y_shape)
  
      i = constant_op.constant([1, 0, 4, 3], dtype=dtypes.int64)
      y_tf = image_ops.pad_to_bounding_box(x, i[0], i[1], i[2], i[3])
      with self.test_session(use_gpu=True):
        self.assertAllClose(y, y_tf.eval())
  
    def testNoOp(self):
      x_shape = [10, 10, 10]
      x = np.random.uniform(size=x_shape)
      offset_height, offset_width = [0, 0]
      self._assertReturns(x, x_shape, offset_height, offset_width, x, x_shape)
  
    def testPadding(self):
      x = [1, 2, 3, 4, 5, 6, 7, 8, 9]
      x_shape = [3, 3, 1]
  
      offset_height, offset_width = [1, 0]
      y = [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
      y_shape = [4, 3, 1]
      self._assertReturns(x, x_shape, offset_height, offset_width, y, y_shape)
  
      offset_height, offset_width = [0, 1]
      y = [0, 1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9]
      y_shape = [3, 4, 1]
      self._assertReturns(x, x_shape, offset_height, offset_width, y, y_shape)
  
      offset_height, offset_width = [0, 0]
      y = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0]
      y_shape = [4, 3, 1]
      self._assertReturns(x, x_shape, offset_height, offset_width, y, y_shape)
  
      offset_height, offset_width = [0, 0]
      y = [1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 0]
      y_shape = [3, 4, 1]
      self._assertReturns(x, x_shape, offset_height, offset_width, y, y_shape)
  
    def testShapeInference(self):
      self._assertShapeInference([55, 66, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([50, 60, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([None, 66, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([None, 60, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([55, None, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([50, None, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([None, None, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([55, 66, None], 55, 66, [55, 66, None])
      self._assertShapeInference([50, 60, None], 55, 66, [55, 66, None])
      self._assertShapeInference([None, None, None], 55, 66, [55, 66, None])
      self._assertShapeInference(None, 55, 66, [55, 66, None])
  
    def testNon3DInput(self):
      # Input image is not 3D
      x = [0] * 15
      offset_height, offset_width = [0, 0]
      target_height, target_width = [2, 2]
  
      for x_shape in ([3, 5], [1, 3, 5, 1, 1]):
        self._assertRaises(x, x_shape, offset_height, offset_width, target_height,
                           target_width,
                           "'image' must have either 3 or 4 dimensions.")
  
    def testZeroLengthInput(self):
      # Input image has 0-length dimension(s).
      # Each line is a test configuration:
      #   x_shape, target_height, target_width
      test_config = (([0, 2, 2], 2, 2), ([2, 0, 2], 2, 2), ([2, 2, 0], 2, 2))
      offset_height, offset_width = [0, 0]
      x = []
  
      for x_shape, target_height, target_width in test_config:
        self._assertRaises(
            x,
            x_shape,
            offset_height,
            offset_width,
            target_height,
            target_width,
            "all dims of 'image.shape' must be > 0",
            use_tensor_inputs_options=[False])
  
        # The original error message does not contain back slashes. However, they
        # are added by either the assert op or the runtime. If this behavior
        # changes in the future, the match string will also needs to be changed.
        self._assertRaises(
            x,
            x_shape,
            offset_height,
            offset_width,
            target_height,
            target_width,
            "all dims of \\'image.shape\\' must be > 0",
            use_tensor_inputs_options=[True])
  
    def testBadParams(self):
      x_shape = [3, 3, 1]
      x = np.zeros(x_shape)
  
      # Each line is a test configuration:
      #   offset_height, offset_width, target_height, target_width, err_msg
      test_config = ((-1, 0, 4, 4, "offset_height must be >= 0"),
                     (0, -1, 4, 4, "offset_width must be >= 0"),
                     (2, 0, 4, 4, "height must be <= target - offset"),
                     (0, 2, 4, 4, "width must be <= target - offset"))
  
      for config_item in test_config:
        self._assertRaises(x, x_shape, *config_item)
  
    def testNameScope(self):
      image = array_ops.placeholder(dtypes.float32, shape=[55, 66, 3])
      y = image_ops.pad_to_bounding_box(image, 0, 0, 55, 66)
      self.assertTrue(y.op.name.startswith("pad_to_bounding_box"))
  
  
  class SelectDistortedCropBoxTest(test_util.TensorFlowTestCase):
  
    def _testSampleDistortedBoundingBox(self, image, bounding_box,
                                        min_object_covered, aspect_ratio_range,
                                        area_range):
      original_area = float(np.prod(image.shape))
      bounding_box_area = float((bounding_box[3] - bounding_box[1]) *
                                (bounding_box[2] - bounding_box[0]))
  
      image_size_np = np.array(image.shape, dtype=np.int32)
      bounding_box_np = (
          np.array(bounding_box, dtype=np.float32).reshape([1, 1, 4]))
  
      aspect_ratios = []
      area_ratios = []
  
      fraction_object_covered = []
  
      num_iter = 1000
      with self.test_session(use_gpu=True):
        image_tf = constant_op.constant(image, shape=image.shape)
        image_size_tf = constant_op.constant(
            image_size_np, shape=image_size_np.shape)
        bounding_box_tf = constant_op.constant(
            bounding_box_np, dtype=dtypes.float32, shape=bounding_box_np.shape)
  
        begin, size, _ = image_ops.sample_distorted_bounding_box(
            image_size=image_size_tf,
            bounding_boxes=bounding_box_tf,
            min_object_covered=min_object_covered,
            aspect_ratio_range=aspect_ratio_range,
            area_range=area_range)
        y = array_ops.strided_slice(image_tf, begin, begin + size)
  
        for _ in xrange(num_iter):
          y_tf = y.eval()
          crop_height = y_tf.shape[0]
          crop_width = y_tf.shape[1]
          aspect_ratio = float(crop_width) / float(crop_height)
          area = float(crop_width * crop_height)
  
          aspect_ratios.append(aspect_ratio)
          area_ratios.append(area / original_area)
          fraction_object_covered.append(float(np.sum(y_tf)) / bounding_box_area)
  
        # min_object_covered as tensor
        min_object_covered_placeholder = array_ops.placeholder(dtypes.float32)
        begin, size, _ = image_ops.sample_distorted_bounding_box(
            image_size=image_size_tf,
            bounding_boxes=bounding_box_tf,
            min_object_covered=min_object_covered_placeholder,
            aspect_ratio_range=aspect_ratio_range,
            area_range=area_range)
        y = array_ops.strided_slice(image_tf, begin, begin + size)
  
        for _ in xrange(num_iter):
          y_tf = y.eval(feed_dict={
              min_object_covered_placeholder: min_object_covered
          })
          crop_height = y_tf.shape[0]
          crop_width = y_tf.shape[1]
          aspect_ratio = float(crop_width) / float(crop_height)
          area = float(crop_width * crop_height)
  
          aspect_ratios.append(aspect_ratio)
          area_ratios.append(area / original_area)
          fraction_object_covered.append(float(np.sum(y_tf)) / bounding_box_area)
  
      # Ensure that each entry is observed within 3 standard deviations.
      # num_bins = 10
      # aspect_ratio_hist, _ = np.histogram(aspect_ratios,
      #                                     bins=num_bins,
      #                                     range=aspect_ratio_range)
      # mean = np.mean(aspect_ratio_hist)
      # stddev = np.sqrt(mean)
      # TODO(wicke, shlens, dga): Restore this test so that it is no longer flaky.
      # TODO(irving): Since the rejection probability is not independent of the
      # aspect ratio, the aspect_ratio random value is not exactly uniformly
      # distributed in [min_aspect_ratio, max_aspect_ratio).  This test should be
      # fixed to reflect the true statistical property, then tightened to enforce
      # a stricter bound.  Or, ideally, the sample_distorted_bounding_box Op
      # be fixed to not use rejection sampling and generate correctly uniform
      # aspect ratios.
      # self.assertAllClose(aspect_ratio_hist,
      #                     [mean] * num_bins, atol=3.6 * stddev)
  
      # The resulting crop will not be uniformly distributed in area. In practice,
      # we find that the area skews towards the small sizes. Instead, we perform
      # a weaker test to ensure that the area ratios are merely within the
      # specified bounds.
      self.assertLessEqual(max(area_ratios), area_range[1])
      self.assertGreaterEqual(min(area_ratios), area_range[0])
  
      # For reference, here is what the distribution of area ratios look like.
      area_ratio_hist, _ = np.histogram(area_ratios, bins=10, range=area_range)
      print("area_ratio_hist ", area_ratio_hist)
  
      # Ensure that fraction_object_covered is satisfied.
      # TODO(wicke, shlens, dga): Restore this test so that it is no longer flaky.
      # self.assertGreaterEqual(min(fraction_object_covered), min_object_covered)
  
    def testWholeImageBoundingBox(self):
      height = 40
      width = 50
      image_size = [height, width, 1]
      bounding_box = [0.0, 0.0, 1.0, 1.0]
      image = np.arange(
          0, np.prod(image_size), dtype=np.int32).reshape(image_size)
      self._testSampleDistortedBoundingBox(
          image,
          bounding_box,
          min_object_covered=0.1,
          aspect_ratio_range=(0.75, 1.33),
          area_range=(0.05, 1.0))
  
    def testWithBoundingBox(self):
      height = 40
      width = 50
      x_shape = [height, width, 1]
      image = np.zeros(x_shape, dtype=np.int32)
  
      # Create an object with 1's in a region with area A and require that
      # the total pixel values >= 0.1 * A.
      min_object_covered = 0.1
  
      xmin = 2
      ymin = 3
      xmax = 12
      ymax = 13
      for x in np.arange(xmin, xmax + 1, 1):
        for y in np.arange(ymin, ymax + 1, 1):
          image[x, y] = 1
  
      # Bounding box is specified as (ymin, xmin, ymax, xmax) in
      # relative coordinates.
      bounding_box = (float(ymin) / height, float(xmin) / width,
                      float(ymax) / height, float(xmax) / width)
  
      self._testSampleDistortedBoundingBox(
          image,
          bounding_box=bounding_box,
          min_object_covered=min_object_covered,
          aspect_ratio_range=(0.75, 1.33),
          area_range=(0.05, 1.0))
  
    def testSampleDistortedBoundingBoxShape(self):
      with self.test_session(use_gpu=True):
        image_size = constant_op.constant(
            [40, 50, 1], shape=[3], dtype=dtypes.int32)
        bounding_box = constant_op.constant(
            [[[0.0, 0.0, 1.0, 1.0]]],
            shape=[1, 1, 4],
            dtype=dtypes.float32,
        )
        begin, end, bbox_for_drawing = image_ops.sample_distorted_bounding_box(
            image_size=image_size,
            bounding_boxes=bounding_box,
            min_object_covered=0.1,
            aspect_ratio_range=(0.75, 1.33),
            area_range=(0.05, 1.0))
  
        # Test that the shapes are correct.
        self.assertAllEqual([3], begin.get_shape().as_list())
        self.assertAllEqual([3], end.get_shape().as_list())
        self.assertAllEqual([1, 1, 4], bbox_for_drawing.get_shape().as_list())
        # Actual run to make sure shape is correct inside Compute().
        begin = begin.eval()
        end = end.eval()
        bbox_for_drawing = bbox_for_drawing.eval()
  
        begin, end, bbox_for_drawing = image_ops.sample_distorted_bounding_box(
            image_size=image_size,
            bounding_boxes=bounding_box,
            min_object_covered=array_ops.placeholder(dtypes.float32),
            aspect_ratio_range=(0.75, 1.33),
            area_range=(0.05, 1.0))
  
        # Test that the shapes are correct.
        self.assertAllEqual([3], begin.get_shape().as_list())
        self.assertAllEqual([3], end.get_shape().as_list())
        self.assertAllEqual([1, 1, 4], bbox_for_drawing.get_shape().as_list())
  
    def testDefaultMinObjectCovered(self):
      # By default min_object_covered=0.1 if not provided
      with self.test_session(use_gpu=True):
        image_size = constant_op.constant(
            [40, 50, 1], shape=[3], dtype=dtypes.int32)
        bounding_box = constant_op.constant(
            [[[0.0, 0.0, 1.0, 1.0]]],
            shape=[1, 1, 4],
            dtype=dtypes.float32,
        )
        begin, end, bbox_for_drawing = image_ops.sample_distorted_bounding_box(
            image_size=image_size,
            bounding_boxes=bounding_box,
            aspect_ratio_range=(0.75, 1.33),
            area_range=(0.05, 1.0))
  
        self.assertAllEqual([3], begin.get_shape().as_list())
        self.assertAllEqual([3], end.get_shape().as_list())
        self.assertAllEqual([1, 1, 4], bbox_for_drawing.get_shape().as_list())
        # Actual run to make sure shape is correct inside Compute().
        begin = begin.eval()
        end = end.eval()
        bbox_for_drawing = bbox_for_drawing.eval()
  
  
  class ResizeImagesTest(test_util.TensorFlowTestCase):
  
    OPTIONS = [
        image_ops.ResizeMethod.BILINEAR, image_ops.ResizeMethod.NEAREST_NEIGHBOR,
        image_ops.ResizeMethod.BICUBIC, image_ops.ResizeMethod.AREA
    ]
  
    TYPES = [
        np.uint8, np.int8, np.uint16, np.int16, np.int32, np.int64, np.float16,
        np.float32, np.float64
    ]
  
    def _assertShapeInference(self, pre_shape, size, post_shape):
      # Try single image resize
      single_image = array_ops.placeholder(dtypes.float32, shape=pre_shape)
      y = image_ops.resize_images(single_image, size)
      self.assertEqual(y.get_shape().as_list(), post_shape)
      # Try batch images resize with known batch size
      images = array_ops.placeholder(dtypes.float32, shape=[99] + pre_shape)
      y = image_ops.resize_images(images, size)
      self.assertEqual(y.get_shape().as_list(), [99] + post_shape)
      # Try batch images resize with unknown batch size
      images = array_ops.placeholder(dtypes.float32, shape=[None] + pre_shape)
      y = image_ops.resize_images(images, size)
      self.assertEqual(y.get_shape().as_list(), [None] + post_shape)
  
    def shouldRunOnGPU(self, opt, nptype):
      if (opt == image_ops.ResizeMethod.NEAREST_NEIGHBOR and
          nptype in [np.float32, np.float64]):
        return True
      else:
        return False
  
    def testNoOp(self):
      img_shape = [1, 6, 4, 1]
      single_shape = [6, 4, 1]
      # This test is also conducted with int8, so 127 is the maximum
      # value that can be used.
      data = [
          127, 127, 64, 64, 127, 127, 64, 64, 64, 64, 127, 127, 64, 64, 127, 127,
          50, 50, 100, 100, 50, 50, 100, 100
      ]
      target_height = 6
      target_width = 4
  
      for nptype in self.TYPES:
        img_np = np.array(data, dtype=nptype).reshape(img_shape)
  
        for opt in self.OPTIONS:
          with self.test_session(use_gpu=True) as sess:
            image = constant_op.constant(img_np, shape=img_shape)
            y = image_ops.resize_images(image, [target_height, target_width], opt)
            yshape = array_ops.shape(y)
            resized, newshape = sess.run([y, yshape])
            self.assertAllEqual(img_shape, newshape)
            self.assertAllClose(resized, img_np, atol=1e-5)
  
        # Resizing with a single image must leave the shape unchanged also.
        with self.test_session(use_gpu=True):
          img_single = img_np.reshape(single_shape)
          image = constant_op.constant(img_single, shape=single_shape)
          y = image_ops.resize_images(image, [target_height, target_width],
                                      self.OPTIONS[0])
          yshape = array_ops.shape(y)
          newshape = yshape.eval()
          self.assertAllEqual(single_shape, newshape)
  
    def testTensorArguments(self):
      img_shape = [1, 6, 4, 1]
      single_shape = [6, 4, 1]
      # This test is also conducted with int8, so 127 is the maximum
      # value that can be used.
      data = [
          127, 127, 64, 64, 127, 127, 64, 64, 64, 64, 127, 127, 64, 64, 127, 127,
          50, 50, 100, 100, 50, 50, 100, 100
      ]
      new_size = array_ops.placeholder(dtypes.int32, shape=(2))
  
      img_np = np.array(data, dtype=np.uint8).reshape(img_shape)
  
      for opt in self.OPTIONS:
        with self.test_session(use_gpu=True) as sess:
          image = constant_op.constant(img_np, shape=img_shape)
          y = image_ops.resize_images(image, new_size, opt)
          yshape = array_ops.shape(y)
          resized, newshape = sess.run([y, yshape], {new_size: [6, 4]})
          self.assertAllEqual(img_shape, newshape)
          self.assertAllClose(resized, img_np, atol=1e-5)
  
      # Resizing with a single image must leave the shape unchanged also.
      with self.test_session(use_gpu=True):
        img_single = img_np.reshape(single_shape)
        image = constant_op.constant(img_single, shape=single_shape)
        y = image_ops.resize_images(image, new_size, self.OPTIONS[0])
        yshape = array_ops.shape(y)
        resized, newshape = sess.run([y, yshape], {new_size: [6, 4]})
        self.assertAllEqual(single_shape, newshape)
        self.assertAllClose(resized, img_single, atol=1e-5)
  
      # Incorrect shape.
      with self.assertRaises(ValueError):
        new_size = constant_op.constant(4)
        _ = image_ops.resize_images(image, new_size,
                                    image_ops.ResizeMethod.BILINEAR)
      with self.assertRaises(ValueError):
        new_size = constant_op.constant([4])
        _ = image_ops.resize_images(image, new_size,
                                    image_ops.ResizeMethod.BILINEAR)
      with self.assertRaises(ValueError):
        new_size = constant_op.constant([1, 2, 3])
        _ = image_ops.resize_images(image, new_size,
                                    image_ops.ResizeMethod.BILINEAR)
  
      # Incorrect dtypes.
      with self.assertRaises(ValueError):
        new_size = constant_op.constant([6.0, 4])
        _ = image_ops.resize_images(image, new_size,
                                    image_ops.ResizeMethod.BILINEAR)
      with self.assertRaises(ValueError):
        _ = image_ops.resize_images(image, [6, 4.0],
                                    image_ops.ResizeMethod.BILINEAR)
      with self.assertRaises(ValueError):
        _ = image_ops.resize_images(image, [None, 4],
                                    image_ops.ResizeMethod.BILINEAR)
      with self.assertRaises(ValueError):
        _ = image_ops.resize_images(image, [6, None],
                                    image_ops.ResizeMethod.BILINEAR)
  
    def testReturnDtype(self):
      target_shapes = [[6, 4], [3, 2], [
          array_ops.placeholder(dtypes.int32),
          array_ops.placeholder(dtypes.int32)
      ]]
      for nptype in self.TYPES:
        image = array_ops.placeholder(nptype, shape=[1, 6, 4, 1])
        for opt in self.OPTIONS:
          for target_shape in target_shapes:
            y = image_ops.resize_images(image, target_shape, opt)
            if (opt == image_ops.ResizeMethod.NEAREST_NEIGHBOR or
                target_shape == image.shape[1:3]):
              expected_dtype = image.dtype
            else:
              expected_dtype = dtypes.float32
            self.assertEqual(y.dtype, expected_dtype)
  
    def testSumTensor(self):
      img_shape = [1, 6, 4, 1]
      # This test is also conducted with int8, so 127 is the maximum
      # value that can be used.
      data = [
          127, 127, 64, 64, 127, 127, 64, 64, 64, 64, 127, 127, 64, 64, 127, 127,
          50, 50, 100, 100, 50, 50, 100, 100
      ]
      # Test size where width is specified as a tensor which is a sum
      # of two tensors.
      width_1 = constant_op.constant(1)
      width_2 = constant_op.constant(3)
      width = math_ops.add(width_1, width_2)
      height = constant_op.constant(6)
  
      img_np = np.array(data, dtype=np.uint8).reshape(img_shape)
  
      for opt in self.OPTIONS:
        with self.cached_session() as sess:
          image = constant_op.constant(img_np, shape=img_shape)
          y = image_ops.resize_images(image, [height, width], opt)
          yshape = array_ops.shape(y)
          resized, newshape = sess.run([y, yshape])
          self.assertAllEqual(img_shape, newshape)
          self.assertAllClose(resized, img_np, atol=1e-5)
  
    def testResizeDown(self):
      # This test is also conducted with int8, so 127 is the maximum
      # value that can be used.
      data = [
          127, 127, 64, 64, 127, 127, 64, 64, 64, 64, 127, 127, 64, 64, 127, 127,
          50, 50, 100, 100, 50, 50, 100, 100
      ]
      expected_data = [127, 64, 64, 127, 50, 100]
      target_height = 3
      target_width = 2
  
      # Test out 3-D and 4-D image shapes.
      img_shapes = [[1, 6, 4, 1], [6, 4, 1]]
      target_shapes = [[1, target_height, target_width, 1],
                       [target_height, target_width, 1]]
  
      for target_shape, img_shape in zip(target_shapes, img_shapes):
  
        for nptype in self.TYPES:
          img_np = np.array(data, dtype=nptype).reshape(img_shape)
  
          for opt in self.OPTIONS:
            if test.is_gpu_available() and self.shouldRunOnGPU(opt, nptype):
              with self.test_session(use_gpu=True):
                image = constant_op.constant(img_np, shape=img_shape)
                y = image_ops.resize_images(image, [target_height, target_width],
                                            opt)
                expected = np.array(expected_data).reshape(target_shape)
                resized = y.eval()
                self.assertAllClose(resized, expected, atol=1e-5)
  
    def testResizeUpAlignCornersFalse(self):
      img_shape = [1, 3, 2, 1]
      data = [64, 32, 32, 64, 50, 100]
      target_height = 6
      target_width = 4
      expected_data = {}
      expected_data[image_ops.ResizeMethod.BILINEAR] = [
          64.0, 48.0, 32.0, 32.0, 48.0, 48.0, 48.0, 48.0, 32.0, 48.0, 64.0, 64.0,
          41.0, 61.5, 82.0, 82.0, 50.0, 75.0, 100.0, 100.0, 50.0, 75.0, 100.0,
          100.0
      ]
      expected_data[image_ops.ResizeMethod.NEAREST_NEIGHBOR] = [
          64.0, 64.0, 32.0, 32.0, 64.0, 64.0, 32.0, 32.0, 32.0, 32.0, 64.0, 64.0,
          32.0, 32.0, 64.0, 64.0, 50.0, 50.0, 100.0, 100.0, 50.0, 50.0, 100.0,
          100.0
      ]
      expected_data[image_ops.ResizeMethod.AREA] = [
          64.0, 64.0, 32.0, 32.0, 64.0, 64.0, 32.0, 32.0, 32.0, 32.0, 64.0, 64.0,
          32.0, 32.0, 64.0, 64.0, 50.0, 50.0, 100.0, 100.0, 50.0, 50.0, 100.0,
          100.0
      ]
  
      for nptype in self.TYPES:
        for opt in [
            image_ops.ResizeMethod.BILINEAR,
            image_ops.ResizeMethod.NEAREST_NEIGHBOR, image_ops.ResizeMethod.AREA
        ]:
          with self.test_session(use_gpu=True):
            img_np = np.array(data, dtype=nptype).reshape(img_shape)
            image = constant_op.constant(img_np, shape=img_shape)
            y = image_ops.resize_images(
                image, [target_height, target_width], opt, align_corners=False)
            resized = y.eval()
            expected = np.array(expected_data[opt]).reshape(
                [1, target_height, target_width, 1])
            self.assertAllClose(resized, expected, atol=1e-05)
  
    def testResizeUpAlignCornersTrue(self):
      img_shape = [1, 3, 2, 1]
      data = [6, 3, 3, 6, 6, 9]
      target_height = 5
      target_width = 4
      expected_data = {}
      expected_data[image_ops.ResizeMethod.BILINEAR] = [
          6.0, 5.0, 4.0, 3.0, 4.5, 4.5, 4.5, 4.5, 3.0, 4.0, 5.0, 6.0, 4.5, 5.5,
          6.5, 7.5, 6.0, 7.0, 8.0, 9.0
      ]
      expected_data[image_ops.ResizeMethod.NEAREST_NEIGHBOR] = [
          6.0, 6.0, 3.0, 3.0, 3.0, 3.0, 6.0, 6.0, 3.0, 3.0, 6.0, 6.0, 6.0, 6.0,
          9.0, 9.0, 6.0, 6.0, 9.0, 9.0
      ]
      # TODO(b/37749740): Improve alignment of ResizeMethod.AREA when
      # align_corners=True.
      expected_data[image_ops.ResizeMethod.AREA] = [
          6.0, 6.0, 6.0, 3.0, 6.0, 6.0, 6.0, 3.0, 3.0, 3.0, 3.0, 6.0, 3.0, 3.0,
          3.0, 6.0, 6.0, 6.0, 6.0, 9.0
      ]
  
      for nptype in self.TYPES:
        for opt in [
            image_ops.ResizeMethod.BILINEAR,
            image_ops.ResizeMethod.NEAREST_NEIGHBOR, image_ops.ResizeMethod.AREA
        ]:
          with self.test_session(use_gpu=True):
            img_np = np.array(data, dtype=nptype).reshape(img_shape)
            image = constant_op.constant(img_np, shape=img_shape)
            y = image_ops.resize_images(
                image, [target_height, target_width], opt, align_corners=True)
            resized = y.eval()
            expected = np.array(expected_data[opt]).reshape(
                [1, target_height, target_width, 1])
            self.assertAllClose(resized, expected, atol=1e-05)
  
    def testResizeUpBicubic(self):
      img_shape = [1, 6, 6, 1]
      data = [
          128, 128, 64, 64, 128, 128, 64, 64, 64, 64, 128, 128, 64, 64, 128, 128,
          50, 50, 100, 100, 50, 50, 100, 100, 50, 50, 100, 100, 50, 50, 100, 100,
          50, 50, 100, 100
      ]
      img_np = np.array(data, dtype=np.uint8).reshape(img_shape)
  
      target_height = 8
      target_width = 8
      expected_data = [
          128, 135, 96, 55, 64, 114, 134, 128, 78, 81, 68, 52, 57, 118, 144, 136,
          55, 49, 79, 109, 103, 89, 83, 84, 74, 70, 95, 122, 115, 69, 49, 55, 100,
          105, 75, 43, 50, 89, 105, 100, 57, 54, 74, 96, 91, 65, 55, 58, 70, 69,
          75, 81, 80, 72, 69, 70, 105, 112, 75, 36, 45, 92, 111, 105
      ]
  
      with self.test_session(use_gpu=True):
        image = constant_op.constant(img_np, shape=img_shape)
        y = image_ops.resize_images(image, [target_height, target_width],
                                    image_ops.ResizeMethod.BICUBIC)
        resized = y.eval()
        expected = np.array(expected_data).reshape(
            [1, target_height, target_width, 1])
        self.assertAllClose(resized, expected, atol=1)
  
    def testResizeDownArea(self):
      img_shape = [1, 6, 6, 1]
      data = [
          128, 64, 32, 16, 8, 4, 4, 8, 16, 32, 64, 128, 128, 64, 32, 16, 8, 4, 5,
          10, 15, 20, 25, 30, 30, 25, 20, 15, 10, 5, 5, 10, 15, 20, 25, 30
      ]
      img_np = np.array(data, dtype=np.uint8).reshape(img_shape)
  
      target_height = 4
      target_width = 4
      expected_data = [
          73, 33, 23, 39, 73, 33, 23, 39, 14, 16, 19, 21, 14, 16, 19, 21
      ]
  
      with self.test_session(use_gpu=True):
        image = constant_op.constant(img_np, shape=img_shape)
        y = image_ops.resize_images(image, [target_height, target_width],
                                    image_ops.ResizeMethod.AREA)
        expected = np.array(expected_data).reshape(
            [1, target_height, target_width, 1])
        resized = y.eval()
        self.assertAllClose(resized, expected, atol=1)
  
    def testCompareNearestNeighbor(self):
      if test.is_gpu_available():
        input_shape = [1, 5, 6, 3]
        target_height = 8
        target_width = 12
        for nptype in [np.float32, np.float64]:
          for align_corners in [True, False]:
            img_np = np.arange(
                0, np.prod(input_shape), dtype=nptype).reshape(input_shape)
            with self.test_session(use_gpu=True):
              image = constant_op.constant(img_np, shape=input_shape)
              new_size = constant_op.constant([target_height, target_width])
              out_op = image_ops.resize_images(
                  image,
                  new_size,
                  image_ops.ResizeMethod.NEAREST_NEIGHBOR,
                  align_corners=align_corners)
              gpu_val = out_op.eval()
            with self.test_session(use_gpu=False):
              image = constant_op.constant(img_np, shape=input_shape)
              new_size = constant_op.constant([target_height, target_width])
              out_op = image_ops.resize_images(
                  image,
                  new_size,
                  image_ops.ResizeMethod.NEAREST_NEIGHBOR,
                  align_corners=align_corners)
              cpu_val = out_op.eval()
            self.assertAllClose(cpu_val, gpu_val, rtol=1e-5, atol=1e-5)
  
    def testCompareBilinear(self):
      if test.is_gpu_available():
        input_shape = [1, 5, 6, 3]
        target_height = 8
        target_width = 12
        for nptype in [np.float32, np.float64]:
          for align_corners in [True, False]:
            img_np = np.arange(
                0, np.prod(input_shape), dtype=nptype).reshape(input_shape)
            value = {}
            for use_gpu in [True, False]:
              with self.test_session(use_gpu=use_gpu):
                image = constant_op.constant(img_np, shape=input_shape)
                new_size = constant_op.constant([target_height, target_width])
                out_op = image_ops.resize_images(
                    image,
                    new_size,
                    image_ops.ResizeMethod.BILINEAR,
                    align_corners=align_corners)
                value[use_gpu] = out_op.eval()
            self.assertAllClose(value[True], value[False], rtol=1e-5, atol=1e-5)
  
    def testShapeInference(self):
      self._assertShapeInference([50, 60, 3], [55, 66], [55, 66, 3])
      self._assertShapeInference([55, 66, 3], [55, 66], [55, 66, 3])
      self._assertShapeInference([59, 69, 3], [55, 66], [55, 66, 3])
      self._assertShapeInference([50, 69, 3], [55, 66], [55, 66, 3])
      self._assertShapeInference([59, 60, 3], [55, 66], [55, 66, 3])
      self._assertShapeInference([None, 60, 3], [55, 66], [55, 66, 3])
      self._assertShapeInference([None, 66, 3], [55, 66], [55, 66, 3])
      self._assertShapeInference([None, 69, 3], [55, 66], [55, 66, 3])
      self._assertShapeInference([50, None, 3], [55, 66], [55, 66, 3])
      self._assertShapeInference([55, None, 3], [55, 66], [55, 66, 3])
      self._assertShapeInference([59, None, 3], [55, 66], [55, 66, 3])
      self._assertShapeInference([None, None, 3], [55, 66], [55, 66, 3])
      self._assertShapeInference([50, 60, None], [55, 66], [55, 66, None])
      self._assertShapeInference([55, 66, None], [55, 66], [55, 66, None])
      self._assertShapeInference([59, 69, None], [55, 66], [55, 66, None])
      self._assertShapeInference([50, 69, None], [55, 66], [55, 66, None])
      self._assertShapeInference([59, 60, None], [55, 66], [55, 66, None])
      self._assertShapeInference([None, None, None], [55, 66], [55, 66, None])
  
    def testNameScope(self):
      img_shape = [1, 3, 2, 1]
      with self.test_session(use_gpu=True):
        single_image = array_ops.placeholder(dtypes.float32, shape=[50, 60, 3])
        y = image_ops.resize_images(single_image, [55, 66])
        self.assertTrue(y.op.name.startswith("resize_images"))
  
    def _ResizeImageCall(self, x, max_h, max_w, preserve_aspect_ratio,
                         use_tensor_inputs):
      if use_tensor_inputs:
        target_max = ops.convert_to_tensor([max_h, max_w])
        x_tensor = array_ops.placeholder(x.dtype, shape=[None] * x.ndim)
        feed_dict = {x_tensor: x}
      else:
        target_max = [max_h, max_w]
        x_tensor = x
        feed_dict = {}
  
      y = image_ops.resize_images(x_tensor, target_max,
                                  preserve_aspect_ratio=preserve_aspect_ratio)
  
      with self.test_session(use_gpu=True):
        return y.eval(feed_dict=feed_dict)
  
    def _assertResizeEqual(self, x, x_shape, y, y_shape,
                           preserve_aspect_ratio=True,
                           use_tensor_inputs_options=None):
      use_tensor_inputs_options = use_tensor_inputs_options or [False, True]
      target_height, target_width, _ = y_shape
      x = np.array(x).reshape(x_shape)
      y = np.array(y).reshape(y_shape)
  
      for use_tensor_inputs in use_tensor_inputs_options:
        y_tf = self._ResizeImageCall(x, target_height, target_width,
                                     preserve_aspect_ratio, use_tensor_inputs)
        self.assertAllClose(y, y_tf)
  
    def _assertResizeCheckShape(self, x, x_shape, target_shape,
                                y_shape, preserve_aspect_ratio=True,
                                use_tensor_inputs_options=None):
      use_tensor_inputs_options = use_tensor_inputs_options or [False, True]
      target_height, target_width = target_shape
      x = np.array(x).reshape(x_shape)
      y = np.zeros(y_shape)
  
      for use_tensor_inputs in use_tensor_inputs_options:
        y_tf = self._ResizeImageCall(x, target_height, target_width,
                                     preserve_aspect_ratio, use_tensor_inputs)
        self.assertShapeEqual(y, ops.convert_to_tensor(y_tf))
  
    def testPreserveAspectRatioMultipleImages(self):
      x_shape = [10, 100, 100, 10]
      x = np.random.uniform(size=x_shape)
  
      self._assertResizeCheckShape(x, x_shape, [250, 250], [10, 250, 250, 10],
                                   preserve_aspect_ratio=False)
  
    def testPreserveAspectRatioNoOp(self):
      x_shape = [10, 10, 10]
      x = np.random.uniform(size=x_shape)
  
      self._assertResizeEqual(x, x_shape, x, x_shape)
  
    def testPreserveAspectRatioSmaller(self):
      x_shape = [100, 100, 10]
      x = np.random.uniform(size=x_shape)
  
      self._assertResizeCheckShape(x, x_shape, [75, 50], [50, 50, 10])
  
    def testPreserveAspectRatioSmallerMultipleImages(self):
      x_shape = [10, 100, 100, 10]
      x = np.random.uniform(size=x_shape)
  
      self._assertResizeCheckShape(x, x_shape, [75, 50], [10, 50, 50, 10])
  
    def testPreserveAspectRatioLarger(self):
      x_shape = [100, 100, 10]
      x = np.random.uniform(size=x_shape)
  
      self._assertResizeCheckShape(x, x_shape, [150, 200], [150, 150, 10])
  
    def testPreserveAspectRatioSameRatio(self):
      x_shape = [1920, 1080, 3]
      x = np.random.uniform(size=x_shape)
  
      self._assertResizeCheckShape(x, x_shape, [3840, 2160], [3840, 2160, 3])
  
  
  class ResizeImageWithPadTest(test_util.TensorFlowTestCase):
  
    def _ResizeImageWithPad(self, x, target_height, target_width,
                            use_tensor_inputs):
      if use_tensor_inputs:
        target_height = ops.convert_to_tensor(target_height)
        target_width = ops.convert_to_tensor(target_width)
        x_tensor = array_ops.placeholder(x.dtype, shape=[None] * x.ndim)
        feed_dict = {x_tensor: x}
      else:
        x_tensor = x
        feed_dict = {}
  
      y = image_ops.resize_image_with_pad(x_tensor, target_height,
                                          target_width)
      if not use_tensor_inputs:
        self.assertTrue(y.get_shape().is_fully_defined())
  
      with self.test_session(use_gpu=True):
        return y.eval(feed_dict=feed_dict)
  
    def _assertReturns(self,
                       x,
                       x_shape,
                       y,
                       y_shape,
                       use_tensor_inputs_options=None):
      use_tensor_inputs_options = use_tensor_inputs_options or [False, True]
      target_height, target_width, _ = y_shape
      x = np.array(x).reshape(x_shape)
      y = np.array(y).reshape(y_shape)
  
      for use_tensor_inputs in use_tensor_inputs_options:
        y_tf = self._ResizeImageWithPad(x, target_height, target_width,
                                        use_tensor_inputs)
        self.assertAllClose(y, y_tf)
  
    def _assertRaises(self,
                      x,
                      x_shape,
                      target_height,
                      target_width,
                      err_msg,
                      use_tensor_inputs_options=None):
      use_tensor_inputs_options = use_tensor_inputs_options or [False, True]
      x = np.array(x).reshape(x_shape)
  
      for use_tensor_inputs in use_tensor_inputs_options:
        try:
          self._ResizeImageWithPad(x, target_height, target_width,
                                   use_tensor_inputs)
        except Exception as e:  # pylint: disable=broad-except
          if err_msg not in str(e):
            raise
        else:
          raise AssertionError("Exception not raised: %s" % err_msg)
  
    def _assertShapeInference(self, pre_shape, height, width, post_shape):
      image = array_ops.placeholder(dtypes.float32, shape=pre_shape)
      y = image_ops.resize_image_with_pad(image, height, width)
      self.assertEqual(y.get_shape().as_list(), post_shape)
  
    def testNoOp(self):
      x_shape = [10, 10, 10]
      x = np.random.uniform(size=x_shape)
  
      self._assertReturns(x, x_shape, x, x_shape)
  
    def testPad(self):
      # Reduce vertical dimension
      x = [1, 2, 3, 4, 5, 6, 7, 8]
      x_shape = [2, 4, 1]
  
      y = [0, 1, 3, 0]
      y_shape = [1, 4, 1]
  
      self._assertReturns(x, x_shape, y, y_shape)
  
      # Reduce horizontal dimension
      x = [1, 2, 3, 4, 5, 6, 7, 8]
      x_shape = [2, 4, 1]
  
      y = [1, 3, 0, 0]
      y_shape = [2, 2, 1]
  
      self._assertReturns(x, x_shape, y, y_shape)
  
      x = [1, 2, 3, 4, 5, 6, 7, 8]
      x_shape = [2, 4, 1]
  
      y = [1, 3]
      y_shape = [1, 2, 1]
  
      self._assertReturns(x, x_shape, y, y_shape)
  
  
  class ResizeImageWithCropOrPadTest(test_util.TensorFlowTestCase):
  
    def _ResizeImageWithCropOrPad(self, x, target_height, target_width,
                                  use_tensor_inputs):
      if use_tensor_inputs:
        target_height = ops.convert_to_tensor(target_height)
        target_width = ops.convert_to_tensor(target_width)
        x_tensor = array_ops.placeholder(x.dtype, shape=[None] * x.ndim)
        feed_dict = {x_tensor: x}
      else:
        x_tensor = x
        feed_dict = {}
  
      y = image_ops.resize_image_with_crop_or_pad(x_tensor, target_height,
                                                  target_width)
      if not use_tensor_inputs:
        self.assertTrue(y.get_shape().is_fully_defined())
  
      with self.test_session(use_gpu=True):
        return y.eval(feed_dict=feed_dict)
  
    def _assertReturns(self,
                       x,
                       x_shape,
                       y,
                       y_shape,
                       use_tensor_inputs_options=None):
      use_tensor_inputs_options = use_tensor_inputs_options or [False, True]
      target_height, target_width, _ = y_shape
      x = np.array(x).reshape(x_shape)
      y = np.array(y).reshape(y_shape)
  
      for use_tensor_inputs in use_tensor_inputs_options:
        y_tf = self._ResizeImageWithCropOrPad(x, target_height, target_width,
                                              use_tensor_inputs)
        self.assertAllClose(y, y_tf)
  
    def _assertRaises(self,
                      x,
                      x_shape,
                      target_height,
                      target_width,
                      err_msg,
                      use_tensor_inputs_options=None):
      use_tensor_inputs_options = use_tensor_inputs_options or [False, True]
      x = np.array(x).reshape(x_shape)
  
      for use_tensor_inputs in use_tensor_inputs_options:
        try:
          self._ResizeImageWithCropOrPad(x, target_height, target_width,
                                         use_tensor_inputs)
        except Exception as e:
          if err_msg not in str(e):
            raise
        else:
          raise AssertionError("Exception not raised: %s" % err_msg)
  
    def _assertShapeInference(self, pre_shape, height, width, post_shape):
      image = array_ops.placeholder(dtypes.float32, shape=pre_shape)
      y = image_ops.resize_image_with_crop_or_pad(image, height, width)
      self.assertEqual(y.get_shape().as_list(), post_shape)
  
    def testNoOp(self):
      x_shape = [10, 10, 10]
      x = np.random.uniform(size=x_shape)
  
      self._assertReturns(x, x_shape, x, x_shape)
  
    def testPad(self):
      # Pad even along col.
      x = [1, 2, 3, 4, 5, 6, 7, 8]
      x_shape = [2, 4, 1]
  
      y = [0, 1, 2, 3, 4, 0, 0, 5, 6, 7, 8, 0]
      y_shape = [2, 6, 1]
  
      self._assertReturns(x, x_shape, y, y_shape)
  
      # Pad odd along col.
      x = [1, 2, 3, 4, 5, 6, 7, 8]
      x_shape = [2, 4, 1]
  
      y = [0, 1, 2, 3, 4, 0, 0, 0, 5, 6, 7, 8, 0, 0]
      y_shape = [2, 7, 1]
  
      self._assertReturns(x, x_shape, y, y_shape)
  
      # Pad even along row.
      x = [1, 2, 3, 4, 5, 6, 7, 8]
      x_shape = [2, 4, 1]
  
      y = [0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0]
      y_shape = [4, 4, 1]
  
      self._assertReturns(x, x_shape, y, y_shape)
  
      # Pad odd along row.
      x = [1, 2, 3, 4, 5, 6, 7, 8]
      x_shape = [2, 4, 1]
  
      y = [0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0]
      y_shape = [5, 4, 1]
  
      self._assertReturns(x, x_shape, y, y_shape)
  
    def testCrop(self):
      # Crop even along col.
      x = [1, 2, 3, 4, 5, 6, 7, 8]
      x_shape = [2, 4, 1]
  
      y = [2, 3, 6, 7]
      y_shape = [2, 2, 1]
  
      self._assertReturns(x, x_shape, y, y_shape)
  
      # Crop odd along col.
      x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
      x_shape = [2, 6, 1]
  
      y = [2, 3, 4, 8, 9, 10]
      y_shape = [2, 3, 1]
  
      self._assertReturns(x, x_shape, y, y_shape)
  
      # Crop even along row.
      x = [1, 2, 3, 4, 5, 6, 7, 8]
      x_shape = [4, 2, 1]
  
      y = [3, 4, 5, 6]
      y_shape = [2, 2, 1]
  
      self._assertReturns(x, x_shape, y, y_shape)
  
      # Crop odd along row.
      x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
      x_shape = [8, 2, 1]
  
      y = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
      y_shape = [5, 2, 1]
  
      self._assertReturns(x, x_shape, y, y_shape)
  
    def testCropAndPad(self):
      # Pad along row but crop along col.
      x = [1, 2, 3, 4, 5, 6, 7, 8]
      x_shape = [2, 4, 1]
  
      y = [0, 0, 2, 3, 6, 7, 0, 0]
      y_shape = [4, 2, 1]
  
      self._assertReturns(x, x_shape, y, y_shape)
  
      # Crop along row but pad along col.
      x = [1, 2, 3, 4, 5, 6, 7, 8]
      x_shape = [4, 2, 1]
  
      y = [0, 3, 4, 0, 0, 5, 6, 0]
      y_shape = [2, 4, 1]
  
      self._assertReturns(x, x_shape, y, y_shape)
  
    def testShapeInference(self):
      self._assertShapeInference([50, 60, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([55, 66, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([59, 69, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([50, 69, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([59, 60, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([None, 60, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([None, 66, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([None, 69, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([50, None, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([55, None, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([59, None, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([None, None, 3], 55, 66, [55, 66, 3])
      self._assertShapeInference([50, 60, None], 55, 66, [55, 66, None])
      self._assertShapeInference([55, 66, None], 55, 66, [55, 66, None])
      self._assertShapeInference([59, 69, None], 55, 66, [55, 66, None])
      self._assertShapeInference([50, 69, None], 55, 66, [55, 66, None])
      self._assertShapeInference([59, 60, None], 55, 66, [55, 66, None])
      self._assertShapeInference([None, None, None], 55, 66, [55, 66, None])
      self._assertShapeInference(None, 55, 66, [55, 66, None])
  
    def testNon3DInput(self):
      # Input image is not 3D
      x = [0] * 15
      target_height, target_width = [4, 4]
  
      for x_shape in ([3, 5],):
        self._assertRaises(x, x_shape, target_height, target_width,
                           "'image' must have either 3 or 4 dimensions.")
  
      for x_shape in ([1, 3, 5, 1, 1],):
        self._assertRaises(x, x_shape, target_height, target_width,
                           "'image' must have either 3 or 4 dimensions.")
  
    def testZeroLengthInput(self):
      # Input image has 0-length dimension(s).
      target_height, target_width = [1, 1]
      x = []
  
      for x_shape in ([0, 2, 2], [2, 0, 2], [2, 2, 0]):
        self._assertRaises(
            x,
            x_shape,
            target_height,
            target_width,
            "all dims of 'image.shape' must be > 0",
            use_tensor_inputs_options=[False])
  
        # The original error message does not contain back slashes. However, they
        # are added by either the assert op or the runtime. If this behavior
        # changes in the future, the match string will also needs to be changed.
        self._assertRaises(
            x,
            x_shape,
            target_height,
            target_width,
            "all dims of \\'image.shape\\' must be > 0",
            use_tensor_inputs_options=[True])
  
    def testBadParams(self):
      x_shape = [4, 4, 1]
      x = np.zeros(x_shape)
  
      # target_height <= 0
      target_height, target_width = [0, 5]
      self._assertRaises(x, x_shape, target_height, target_width,
                         "target_height must be > 0")
  
      # target_width <= 0
      target_height, target_width = [5, 0]
      self._assertRaises(x, x_shape, target_height, target_width,
                         "target_width must be > 0")
  
    def testNameScope(self):
      image = array_ops.placeholder(dtypes.float32, shape=[50, 60, 3])
      y = image_ops.resize_image_with_crop_or_pad(image, 55, 66)
      self.assertTrue(y.op.name.startswith("resize_image_with_crop_or_pad"))
  
  
  def _SimpleColorRamp():
    """Build a simple color ramp RGB image."""
    w, h = 256, 200
    i = np.arange(h)[:, None]
    j = np.arange(w)
    image = np.empty((h, w, 3), dtype=np.uint8)
    image[:, :, 0] = i
    image[:, :, 1] = j
    image[:, :, 2] = (i + j) >> 1
    return image
  
  
  class JpegTest(test_util.TensorFlowTestCase):
  
    # TODO(irving): Add self.assertAverageLess or similar to test_util
    def averageError(self, image0, image1):
      self.assertEqual(image0.shape, image1.shape)
      image0 = image0.astype(int)  # Avoid overflow
      return np.abs(image0 - image1).sum() / np.prod(image0.shape)
  
    def testExisting(self):
      # Read a real jpeg and verify shape
      path = ("tensorflow/core/lib/jpeg/testdata/"
              "jpeg_merge_test1.jpg")
      with self.test_session(use_gpu=True) as sess:
        jpeg0 = io_ops.read_file(path)
        image0 = image_ops.decode_jpeg(jpeg0)
        image1 = image_ops.decode_jpeg(image_ops.encode_jpeg(image0))
        jpeg0, image0, image1 = sess.run([jpeg0, image0, image1])
        self.assertEqual(len(jpeg0), 3771)
        self.assertEqual(image0.shape, (256, 128, 3))
        self.assertLess(self.averageError(image0, image1), 1.4)
  
    def testCmyk(self):
      # Confirm that CMYK reads in as RGB
      base = "tensorflow/core/lib/jpeg/testdata"
      rgb_path = os.path.join(base, "jpeg_merge_test1.jpg")
      cmyk_path = os.path.join(base, "jpeg_merge_test1_cmyk.jpg")
      shape = 256, 128, 3
      for channels in 3, 0:
        with self.test_session(use_gpu=True) as sess:
          rgb = image_ops.decode_jpeg(
              io_ops.read_file(rgb_path), channels=channels)
          cmyk = image_ops.decode_jpeg(
              io_ops.read_file(cmyk_path), channels=channels)
          rgb, cmyk = sess.run([rgb, cmyk])
          self.assertEqual(rgb.shape, shape)
          self.assertEqual(cmyk.shape, shape)
          error = self.averageError(rgb, cmyk)
          self.assertLess(error, 4)
  
    def testCropAndDecodeJpeg(self):
      with self.cached_session() as sess:
        # Encode it, then decode it, then encode it
        base = "tensorflow/core/lib/jpeg/testdata"
        jpeg0 = io_ops.read_file(os.path.join(base, "jpeg_merge_test1.jpg"))
  
        h, w, _ = 256, 128, 3
        crop_windows = [[0, 0, 5, 5], [0, 0, 5, w], [0, 0, h, 5],
                        [h - 6, w - 5, 6, 5], [6, 5, 15, 10], [0, 0, h, w]]
        for crop_window in crop_windows:
          # Explicit two stages: decode + crop.
          image1 = image_ops.decode_jpeg(jpeg0)
          y, x, h, w = crop_window
          image1_crop = image_ops.crop_to_bounding_box(image1, y, x, h, w)
  
          # Combined decode+crop.
          image2 = image_ops.decode_and_crop_jpeg(jpeg0, crop_window)
  
          # Combined decode+crop should have the same shape inference
          self.assertAllEqual(image1_crop.get_shape().as_list(),
                              image2.get_shape().as_list())
  
          # CropAndDecode should be equal to DecodeJpeg+Crop.
          image1_crop, image2 = sess.run([image1_crop, image2])
          self.assertAllEqual(image1_crop, image2)
  
    def testCropAndDecodeJpegWithInvalidCropWindow(self):
      with self.cached_session() as sess:
        # Encode it, then decode it, then encode it
        base = "tensorflow/core/lib/jpeg/testdata"
        jpeg0 = io_ops.read_file(os.path.join(base, "jpeg_merge_test1.jpg"))
  
        h, w, _ = 256, 128, 3
        # Invalid crop windows.
        crop_windows = [[-1, 11, 11, 11], [11, -1, 11, 11], [11, 11, -1, 11],
                        [11, 11, 11, -1], [11, 11, 0, 11], [11, 11, 11, 0],
                        [0, 0, h + 1, w], [0, 0, h, w + 1]]
        for crop_window in crop_windows:
          result = image_ops.decode_and_crop_jpeg(jpeg0, crop_window)
          with self.assertRaisesWithPredicateMatch(
              errors.InvalidArgumentError,
              lambda e: "Invalid JPEG data or crop window" in str(e)):
            sess.run(result)
  
    def testSynthetic(self):
      with self.test_session(use_gpu=True) as sess:
        # Encode it, then decode it, then encode it
        image0 = constant_op.constant(_SimpleColorRamp())
        jpeg0 = image_ops.encode_jpeg(image0)
        image1 = image_ops.decode_jpeg(jpeg0, dct_method="INTEGER_ACCURATE")
        image2 = image_ops.decode_jpeg(
            image_ops.encode_jpeg(image1), dct_method="INTEGER_ACCURATE")
        jpeg0, image0, image1, image2 = sess.run([jpeg0, image0, image1, image2])
  
        # The decoded-encoded image should be similar to the input
        self.assertLess(self.averageError(image0, image1), 0.6)
  
        # We should be very close to a fixpoint
        self.assertLess(self.averageError(image1, image2), 0.02)
  
        # Smooth ramps compress well (input size is 153600)
        self.assertGreaterEqual(len(jpeg0), 5000)
        self.assertLessEqual(len(jpeg0), 6000)
  
    def testSyntheticFasterAlgorithm(self):
      with self.test_session(use_gpu=True) as sess:
        # Encode it, then decode it, then encode it
        image0 = constant_op.constant(_SimpleColorRamp())
        jpeg0 = image_ops.encode_jpeg(image0)
        image1 = image_ops.decode_jpeg(jpeg0, dct_method="INTEGER_FAST")
        image2 = image_ops.decode_jpeg(
            image_ops.encode_jpeg(image1), dct_method="INTEGER_FAST")
        jpeg0, image0, image1, image2 = sess.run([jpeg0, image0, image1, image2])
  
        # The decoded-encoded image should be similar to the input, but
        # note this is worse than the slower algorithm because it is
        # less accurate.
        self.assertLess(self.averageError(image0, image1), 0.95)
  
        # Repeated compression / decompression will have a higher error
        # with a lossier algorithm.
        self.assertLess(self.averageError(image1, image2), 1.05)
  
        # Smooth ramps compress well (input size is 153600)
        self.assertGreaterEqual(len(jpeg0), 5000)
        self.assertLessEqual(len(jpeg0), 6000)
  
    def testDefaultDCTMethodIsIntegerFast(self):
      with self.test_session(use_gpu=True) as sess:
        # Compare decoding with both dct_option=INTEGER_FAST and
        # default.  They should be the same.
        image0 = constant_op.constant(_SimpleColorRamp())
        jpeg0 = image_ops.encode_jpeg(image0)
        image1 = image_ops.decode_jpeg(jpeg0, dct_method="INTEGER_FAST")
        image2 = image_ops.decode_jpeg(jpeg0)
        image1, image2 = sess.run([image1, image2])
  
        # The images should be the same.
        self.assertAllClose(image1, image2)
  
    def testShape(self):
      with self.test_session(use_gpu=True) as sess:
        jpeg = constant_op.constant("nonsense")
        for channels in 0, 1, 3:
          image = image_ops.decode_jpeg(jpeg, channels=channels)
          self.assertEqual(image.get_shape().as_list(),
                           [None, None, channels or None])
  
    def testExtractJpegShape(self):
      # Read a real jpeg and verify shape.
      path = ("tensorflow/core/lib/jpeg/testdata/"
              "jpeg_merge_test1.jpg")
      with self.test_session(use_gpu=True) as sess:
        jpeg = io_ops.read_file(path)
        # Extract shape without decoding.
        [image_shape] = sess.run([image_ops.extract_jpeg_shape(jpeg)])
        self.assertEqual(image_shape.tolist(), [256, 128, 3])
  
    def testExtractJpegShapeforCmyk(self):
      # Read a cmyk jpeg image, and verify its shape.
      path = ("tensorflow/core/lib/jpeg/testdata/"
              "jpeg_merge_test1_cmyk.jpg")
      with self.test_session(use_gpu=True) as sess:
        jpeg = io_ops.read_file(path)
        [image_shape] = sess.run([image_ops.extract_jpeg_shape(jpeg)])
        # Cmyk jpeg image has 4 channels.
        self.assertEqual(image_shape.tolist(), [256, 128, 4])
  
  
  class PngTest(test_util.TensorFlowTestCase):
  
    def testExisting(self):
      # Read some real PNGs, converting to different channel numbers
      prefix = "tensorflow/core/lib/png/testdata/"
      inputs = ((1, "lena_gray.png"), (4, "lena_rgba.png"),
                (3, "lena_palette.png"), (4, "lena_palette_trns.png"))
      for channels_in, filename in inputs:
        for channels in 0, 1, 3, 4:
          with self.test_session(use_gpu=True) as sess:
            png0 = io_ops.read_file(prefix + filename)
            image0 = image_ops.decode_png(png0, channels=channels)
            png0, image0 = sess.run([png0, image0])
            self.assertEqual(image0.shape, (26, 51, channels or channels_in))
            if channels == channels_in:
              image1 = image_ops.decode_png(image_ops.encode_png(image0))
              self.assertAllEqual(image0, image1.eval())
  
    def testSynthetic(self):
      with self.test_session(use_gpu=True) as sess:
        # Encode it, then decode it
        image0 = constant_op.constant(_SimpleColorRamp())
        png0 = image_ops.encode_png(image0, compression=7)
        image1 = image_ops.decode_png(png0)
        png0, image0, image1 = sess.run([png0, image0, image1])
  
        # PNG is lossless
        self.assertAllEqual(image0, image1)
  
        # Smooth ramps compress well, but not too well
        self.assertGreaterEqual(len(png0), 400)
        self.assertLessEqual(len(png0), 750)
  
    def testSyntheticUint16(self):
      with self.test_session(use_gpu=True) as sess:
        # Encode it, then decode it
        image0 = constant_op.constant(_SimpleColorRamp(), dtype=dtypes.uint16)
        png0 = image_ops.encode_png(image0, compression=7)
        image1 = image_ops.decode_png(png0, dtype=dtypes.uint16)
        png0, image0, image1 = sess.run([png0, image0, image1])
  
        # PNG is lossless
        self.assertAllEqual(image0, image1)
  
        # Smooth ramps compress well, but not too well
        self.assertGreaterEqual(len(png0), 800)
        self.assertLessEqual(len(png0), 1500)
  
    def testSyntheticTwoChannel(self):
      with self.test_session(use_gpu=True) as sess:
        # Strip the b channel from an rgb image to get a two-channel image.
        gray_alpha = _SimpleColorRamp()[:, :, 0:2]
        image0 = constant_op.constant(gray_alpha)
        png0 = image_ops.encode_png(image0, compression=7)
        image1 = image_ops.decode_png(png0)
        png0, image0, image1 = sess.run([png0, image0, image1])
        self.assertEqual(2, image0.shape[-1])
        self.assertAllEqual(image0, image1)
  
    def testSyntheticTwoChannelUint16(self):
      with self.test_session(use_gpu=True) as sess:
        # Strip the b channel from an rgb image to get a two-channel image.
        gray_alpha = _SimpleColorRamp()[:, :, 0:2]
        image0 = constant_op.constant(gray_alpha, dtype=dtypes.uint16)
        png0 = image_ops.encode_png(image0, compression=7)
        image1 = image_ops.decode_png(png0, dtype=dtypes.uint16)
        png0, image0, image1 = sess.run([png0, image0, image1])
        self.assertEqual(2, image0.shape[-1])
        self.assertAllEqual(image0, image1)
  
    def testShape(self):
      with self.test_session(use_gpu=True):
        png = constant_op.constant("nonsense")
        for channels in 0, 1, 3:
          image = image_ops.decode_png(png, channels=channels)
          self.assertEqual(image.get_shape().as_list(),
                           [None, None, channels or None])
  
  
  class GifTest(test_util.TensorFlowTestCase):
  
    def _testValid(self, filename):
      # Read some real GIFs
      prefix = "tensorflow/core/lib/gif/testdata/"
      WIDTH = 20
      HEIGHT = 40
      STRIDE = 5
      shape = (12, HEIGHT, WIDTH, 3)
  
      with self.test_session(use_gpu=True) as sess:
        gif0 = io_ops.read_file(prefix + filename)
        image0 = image_ops.decode_gif(gif0)
        gif0, image0 = sess.run([gif0, image0])
  
        self.assertEqual(image0.shape, shape)
  
        for frame_idx, frame in enumerate(image0):
          gt = np.zeros(shape[1:], dtype=np.uint8)
          start = frame_idx * STRIDE
          end = (frame_idx + 1) * STRIDE
          print(frame_idx)
          if end <= WIDTH:
            gt[:, start:end, :] = 255
          else:
            start -= WIDTH
            end -= WIDTH
            gt[start:end, :, :] = 255
  
          self.assertAllClose(frame, gt)
  
    def testValid(self):
      self._testValid("scan.gif")
      self._testValid("optimized.gif")
  
    def testShape(self):
      with self.test_session(use_gpu=True) as sess:
        gif = constant_op.constant("nonsense")
        image = image_ops.decode_gif(gif)
        self.assertEqual(image.get_shape().as_list(), [None, None, None, 3])
  
  
  class ConvertImageTest(test_util.TensorFlowTestCase):
  
    def _convert(self, original, original_dtype, output_dtype, expected):
      x_np = np.array(original, dtype=original_dtype.as_numpy_dtype())
      y_np = np.array(expected, dtype=output_dtype.as_numpy_dtype())
  
      with self.test_session(use_gpu=True):
        image = constant_op.constant(x_np)
        y = image_ops.convert_image_dtype(image, output_dtype)
        self.assertTrue(y.dtype == output_dtype)
        self.assertAllClose(y.eval(), y_np, atol=1e-5)
        if output_dtype in [
            dtypes.float32, dtypes.float64, dtypes.int32, dtypes.int64
        ]:
          y_saturate = image_ops.convert_image_dtype(
              image, output_dtype, saturate=True)
          self.assertTrue(y_saturate.dtype == output_dtype)
          self.assertAllClose(y_saturate.eval(), y_np, atol=1e-5)
  
    def testNoConvert(self):
      # Make sure converting to the same data type creates only an identity op
      with self.test_session(use_gpu=True):
        image = constant_op.constant([1], dtype=dtypes.uint8)
        image_ops.convert_image_dtype(image, dtypes.uint8)
        y = image_ops.convert_image_dtype(image, dtypes.uint8)
        self.assertEquals(y.op.type, "Identity")
        self.assertEquals(y.op.inputs[0], image)
  
    def testConvertBetweenInteger(self):
      # Make sure converting to between integer types scales appropriately
      with self.test_session(use_gpu=True):
        self._convert([0, 255], dtypes.uint8, dtypes.int16, [0, 255 * 128])
        self._convert([0, 32767], dtypes.int16, dtypes.uint8, [0, 255])
        self._convert([0, 2**32], dtypes.int64, dtypes.int32, [0, 1])
        self._convert([0, 1], dtypes.int32, dtypes.int64, [0, 2**32])
  
    def testConvertBetweenFloat(self):
      # Make sure converting to between float types does nothing interesting
      with self.test_session(use_gpu=True):
        self._convert([-1.0, 0, 1.0, 200000], dtypes.float32, dtypes.float64,
                      [-1.0, 0, 1.0, 200000])
        self._convert([-1.0, 0, 1.0, 200000], dtypes.float64, dtypes.float32,
                      [-1.0, 0, 1.0, 200000])
  
    def testConvertBetweenIntegerAndFloat(self):
      # Make sure converting from and to a float type scales appropriately
      with self.test_session(use_gpu=True):
        self._convert([0, 1, 255], dtypes.uint8, dtypes.float32,
                      [0, 1.0 / 255.0, 1])
        self._convert([0, 1.1 / 255.0, 1], dtypes.float32, dtypes.uint8,
                      [0, 1, 255])
  
    def testConvertBetweenInt16AndInt8(self):
      with self.test_session(use_gpu=True):
        # uint8, uint16
        self._convert([0, 255 * 256], dtypes.uint16, dtypes.uint8, [0, 255])
        self._convert([0, 255], dtypes.uint8, dtypes.uint16, [0, 255 * 256])
        # int8, uint16
        self._convert([0, 127 * 2 * 256], dtypes.uint16, dtypes.int8, [0, 127])
        self._convert([0, 127], dtypes.int8, dtypes.uint16, [0, 127 * 2 * 256])
        # int16, uint16
        self._convert([0, 255 * 256], dtypes.uint16, dtypes.int16, [0, 255 * 128])
        self._convert([0, 255 * 128], dtypes.int16, dtypes.uint16, [0, 255 * 256])
  
  
  class TotalVariationTest(test_util.TensorFlowTestCase):
    """Tests the function total_variation() in image_ops.
  
    We test a few small handmade examples, as well as
    some larger examples using an equivalent numpy
    implementation of the total_variation() function.
  
    We do NOT test for overflows and invalid / edge-case arguments.
    """
  
    def _test(self, x_np, y_np):
      """Test that the TensorFlow implementation of
      total_variation(x_np) calculates the values in y_np.
  
      Note that these may be float-numbers so we only test
      for approximate equality within some narrow error-bound.
      """
  
      # Create a TensorFlow session.
      with self.test_session(use_gpu=True):
        # Add a constant to the TensorFlow graph that holds the input.
        x_tf = constant_op.constant(x_np, shape=x_np.shape)
  
        # Add ops for calculating the total variation using TensorFlow.
        y = image_ops.total_variation(images=x_tf)
  
        # Run the TensorFlow session to calculate the result.
        y_tf = y.eval()
  
        # Assert that the results are as expected within
        # some small error-bound in case they are float-values.
        self.assertAllClose(y_tf, y_np)
  
    def _total_variation_np(self, x_np):
      """Calculate the total variation of x_np using numpy.
      This implements the same function as TensorFlow but
      using numpy instead.
  
      Args:
          x_np: Numpy array with 3 or 4 dimensions.
      """
  
      dim = len(x_np.shape)
  
      if dim == 3:
        # Calculate differences for neighboring pixel-values using slices.
        dif1 = x_np[1:, :, :] - x_np[:-1, :, :]
        dif2 = x_np[:, 1:, :] - x_np[:, :-1, :]
  
        # Sum for all axis.
        sum_axis = None
      elif dim == 4:
        # Calculate differences for neighboring pixel-values using slices.
        dif1 = x_np[:, 1:, :, :] - x_np[:, :-1, :, :]
        dif2 = x_np[:, :, 1:, :] - x_np[:, :, :-1, :]
  
        # Only sum for the last 3 axis.
        sum_axis = (1, 2, 3)
      else:
        # This should not occur in this test-code.
        pass
  
      tot_var = np.sum(np.abs(dif1), axis=sum_axis) + \
                np.sum(np.abs(dif2), axis=sum_axis)
  
      return tot_var
  
    def _test_tensorflow_vs_numpy(self, x_np):
      """Test the TensorFlow implementation against a numpy implementation.
  
      Args:
          x_np: Numpy array with 3 or 4 dimensions.
      """
  
      # Calculate the y-values using the numpy implementation.
      y_np = self._total_variation_np(x_np)
  
      self._test(x_np, y_np)
  
    def _generateArray(self, shape):
      """Generate an array of the given shape for use in testing.
      The numbers are calculated as the cumulative sum, which
      causes the difference between neighboring numbers to vary."""
  
      # Flattened length of the array.
      flat_len = np.prod(shape)
  
      a = np.array(range(flat_len), dtype=int)
      a = np.cumsum(a)
      a = a.reshape(shape)
  
      return a
  
    def testTotalVariationNumpy(self):
      """Test the TensorFlow implementation against a numpy implementation.
      The two implementations are very similar so it is possible that both
      have the same bug, which would not be detected by this test. It is
      therefore necessary to test with manually crafted data as well."""
  
      # Generate a test-array.
      # This is an 'image' with 100x80 pixels and 3 color channels.
      a = self._generateArray(shape=(100, 80, 3))
  
      # Test the TensorFlow implementation vs. numpy implementation.
      # We use a numpy implementation to check the results that are
      # calculated using TensorFlow are correct.
      self._test_tensorflow_vs_numpy(a)
      self._test_tensorflow_vs_numpy(a + 1)
      self._test_tensorflow_vs_numpy(-a)
      self._test_tensorflow_vs_numpy(1.1 * a)
  
      # Expand to a 4-dim array.
      b = a[np.newaxis, :]
  
      # Combine several variations of the image into a single 4-dim array.
      multi = np.vstack((b, b + 1, -b, 1.1 * b))
  
      # Test that the TensorFlow function can also handle 4-dim arrays.
      self._test_tensorflow_vs_numpy(multi)
  
    def testTotalVariationHandmade(self):
      """Test the total variation for a few handmade examples."""
  
      # We create an image that is 2x2 pixels with 3 color channels.
      # The image is very small so we can check the result by hand.
  
      # Red color channel.
      # The following are the sum of absolute differences between the pixels.
      # sum row dif = (4-1) + (7-2) = 3 + 5 = 8
      # sum col dif = (2-1) + (7-4) = 1 + 3 = 4
      r = [[1, 2], [4, 7]]
  
      # Blue color channel.
      # sum row dif = 18 + 29 = 47
      # sum col dif = 7 + 18 = 25
      g = [[11, 18], [29, 47]]
  
      # Green color channel.
      # sum row dif = 120 + 193 = 313
      # sum col dif = 47 + 120 = 167
      b = [[73, 120], [193, 313]]
  
      # Combine the 3 color channels into a single 3-dim array.
      # The shape is (2, 2, 3) corresponding to (height, width and color).
      a = np.dstack((r, g, b))
  
      # Total variation for this image.
      # Sum of all pixel differences = 8 + 4 + 47 + 25 + 313 + 167 = 564
      tot_var = 564
  
      # Calculate the total variation using TensorFlow and assert it is correct.
      self._test(a, tot_var)
  
      # If we add 1 to all pixel-values then the total variation is unchanged.
      self._test(a + 1, tot_var)
  
      # If we negate all pixel-values then the total variation is unchanged.
      self._test(-a, tot_var)
  
      # Scale the pixel-values by a float. This scales the total variation as well.
      b = 1.1 * a
      self._test(b, 1.1 * tot_var)
  
      # Scale by another float.
      c = 1.2 * a
      self._test(c, 1.2 * tot_var)
  
      # Combine these 3 images into a single array of shape (3, 2, 2, 3)
      # where the first dimension is for the image-number.
      multi = np.vstack((a[np.newaxis, :], b[np.newaxis, :], c[np.newaxis, :]))
  
      # Check that TensorFlow correctly calculates the total variation
      # for each image individually and returns the correct array.
      self._test(multi, tot_var * np.array([1.0, 1.1, 1.2]))
  
  
  class FormatTest(test_util.TensorFlowTestCase):
  
    def testFormats(self):
      prefix = "tensorflow/core/lib"
      paths = ("png/testdata/lena_gray.png", "jpeg/testdata/jpeg_merge_test1.jpg",
               "gif/testdata/lena.gif")
      decoders = {
          "jpeg": functools.partial(image_ops.decode_jpeg, channels=3),
          "png": functools.partial(image_ops.decode_png, channels=3),
          "gif": lambda s: array_ops.squeeze(image_ops.decode_gif(s), axis=0),
      }
      with self.cached_session():
        for path in paths:
          contents = io_ops.read_file(os.path.join(prefix, path)).eval()
          images = {}
          for name, decode in decoders.items():
            image = decode(contents).eval()
            self.assertEqual(image.ndim, 3)
            for prev_name, prev in images.items():
              print("path %s, names %s %s, shapes %s %s" %
                    (path, name, prev_name, image.shape, prev.shape))
              self.assertAllEqual(image, prev)
            images[name] = image
  
    def testError(self):
      path = "tensorflow/core/lib/gif/testdata/scan.gif"
      with self.cached_session():
        for decode in image_ops.decode_jpeg, image_ops.decode_png:
          with self.assertRaisesOpError(r"Got 12 frames"):
            decode(io_ops.read_file(path)).eval()
  
  
  class NonMaxSuppressionTest(test_util.TensorFlowTestCase):
  
    def testSelectFromThreeClusters(self):
      boxes_np = [[0, 0, 1, 1], [0, 0.1, 1, 1.1], [0, -0.1, 1, 0.9],
                  [0, 10, 1, 11], [0, 10.1, 1, 11.1], [0, 100, 1, 101]]
      scores_np = [0.9, 0.75, 0.6, 0.95, 0.5, 0.3]
      max_output_size_np = 3
      iou_threshold_np = 0.5
      with self.cached_session():
        boxes = constant_op.constant(boxes_np)
        scores = constant_op.constant(scores_np)
        max_output_size = constant_op.constant(max_output_size_np)
        iou_threshold = constant_op.constant(iou_threshold_np)
        selected_indices = image_ops.non_max_suppression(
            boxes, scores, max_output_size, iou_threshold).eval()
        self.assertAllClose(selected_indices, [3, 0, 5])
  
    def testInvalidShape(self):
      # The boxes should be 2D of shape [num_boxes, 4].
      with self.assertRaisesRegexp(ValueError,
                                   "Shape must be rank 2 but is rank 1"):
        boxes = constant_op.constant([0.0, 0.0, 1.0, 1.0])
        scores = constant_op.constant([0.9])
        image_ops.non_max_suppression(boxes, scores, 3, 0.5)
  
      with self.assertRaisesRegexp(ValueError, "Dimension must be 4 but is 3"):
        boxes = constant_op.constant([[0.0, 0.0, 1.0]])
        scores = constant_op.constant([0.9])
        image_ops.non_max_suppression(boxes, scores, 3, 0.5)
  
      # The boxes is of shape [num_boxes, 4], and the scores is
      # of shape [num_boxes]. So an error will thrown.
      with self.assertRaisesRegexp(ValueError,
                                   "Dimensions must be equal, but are 1 and 2"):
        boxes = constant_op.constant([[0.0, 0.0, 1.0, 1.0]])
        scores = constant_op.constant([0.9, 0.75])
        selected_indices = image_ops.non_max_suppression(boxes, scores, 3, 0.5)
  
      # The scores should be 1D of shape [num_boxes].
      with self.assertRaisesRegexp(ValueError,
                                   "Shape must be rank 1 but is rank 2"):
        boxes = constant_op.constant([[0.0, 0.0, 1.0, 1.0]])
        scores = constant_op.constant([[0.9]])
        image_ops.non_max_suppression(boxes, scores, 3, 0.5)
  
      # The max_output_size should be a scaler (0-D).
      with self.assertRaisesRegexp(ValueError,
                                   "Shape must be rank 0 but is rank 1"):
        boxes = constant_op.constant([[0.0, 0.0, 1.0, 1.0]])
        scores = constant_op.constant([0.9])
        image_ops.non_max_suppression(boxes, scores, [3], 0.5)
  
      # The iou_threshold should be a scaler (0-D).
      with self.assertRaisesRegexp(ValueError,
                                   "Shape must be rank 0 but is rank 2"):
        boxes = constant_op.constant([[0.0, 0.0, 1.0, 1.0]])
        scores = constant_op.constant([0.9])
        image_ops.non_max_suppression(boxes, scores, 3, [[0.5]])
  
  
  class NonMaxSuppressionPaddedTest(test_util.TensorFlowTestCase):
  
    def testSelectFromThreeClusters(self):
      boxes_np = [[0, 0, 1, 1], [0, 0.1, 1, 1.1], [0, -0.1, 1, 0.9],
                  [0, 10, 1, 11], [0, 10.1, 1, 11.1], [0, 100, 1, 101]]
      scores_np = [0.9, 0.75, 0.6, 0.95, 0.5, 0.3]
      max_output_size_np = 5
      iou_threshold_np = 0.5
      boxes = constant_op.constant(boxes_np)
      scores = constant_op.constant(scores_np)
      max_output_size = constant_op.constant(max_output_size_np)
      iou_threshold = constant_op.constant(iou_threshold_np)
      selected_indices_padded, num_valid_padded = \
          image_ops.non_max_suppression_padded(
              boxes,
              scores,
              max_output_size,
              iou_threshold,
              pad_to_max_output_size=True)
      selected_indices, num_valid = image_ops.non_max_suppression_padded(
          boxes,
          scores,
          max_output_size,
          iou_threshold,
          pad_to_max_output_size=False)
      # The output shape of the padded operation must be fully defined.
      self.assertEqual(selected_indices_padded.shape.is_fully_defined(), True)
      self.assertEqual(selected_indices.shape.is_fully_defined(), False)
      with self.cached_session():
        self.assertAllClose(selected_indices_padded.eval(), [3, 0, 5, 0, 0])
        self.assertEqual(num_valid_padded.eval(), 3)
        self.assertAllClose(selected_indices.eval(), [3, 0, 5])
        self.assertEqual(num_valid.eval(), 3)
  
  
  class VerifyCompatibleImageShapesTest(test_util.TensorFlowTestCase):
    """Tests utility function used by ssim() and psnr()."""
  
    def testWrongDims(self):
      img = array_ops.placeholder(dtype=dtypes.float32)
      img_np = np.array((2, 2))
  
      with self.test_session(use_gpu=True) as sess:
        _, _, checks = image_ops_impl._verify_compatible_image_shapes(img, img)
        with self.assertRaises(errors.InvalidArgumentError):
          sess.run(checks, {img: img_np})
  
    def testShapeMismatch(self):
      img1 = array_ops.placeholder(dtype=dtypes.float32)
      img2 = array_ops.placeholder(dtype=dtypes.float32)
  
      img1_np = np.array([1, 2, 2, 1])
      img2_np = np.array([1, 3, 3, 1])
  
      with self.test_session(use_gpu=True) as sess:
        _, _, checks = image_ops_impl._verify_compatible_image_shapes(img1, img2)
        with self.assertRaises(errors.InvalidArgumentError):
          sess.run(checks, {img1: img1_np, img2: img2_np})
  
  
  class PSNRTest(test_util.TensorFlowTestCase):
    """Tests for PSNR."""
  
    def _LoadTestImage(self, sess, filename):
      content = io_ops.read_file(os.path.join(
          "tensorflow/core/lib/psnr/testdata", filename))
      im = image_ops.decode_jpeg(content, dct_method="INTEGER_ACCURATE")
      im = image_ops.convert_image_dtype(im, dtypes.float32)
      im, = sess.run([im])
      return np.expand_dims(im, axis=0)
  
    def _LoadTestImages(self):
      with self.test_session(use_gpu=True) as sess:
        q20 = self._LoadTestImage(sess, "cat_q20.jpg")
        q72 = self._LoadTestImage(sess, "cat_q72.jpg")
        q95 = self._LoadTestImage(sess, "cat_q95.jpg")
        return q20, q72, q95
  
    def _PSNR_NumPy(self, orig, target, max_value):
      """Numpy implementation of PSNR."""
      mse = ((orig - target) ** 2).mean(axis=(-3, -2, -1))
      return 20 * np.log10(max_value) - 10 * np.log10(mse)
  
    def _RandomImage(self, shape, max_val):
      """Returns an image or image batch with given shape."""
      return np.random.rand(*shape).astype(np.float32) * max_val
  
    def testPSNRSingleImage(self):
      image1 = self._RandomImage((8, 8, 1), 1)
      image2 = self._RandomImage((8, 8, 1), 1)
      psnr = self._PSNR_NumPy(image1, image2, 1)
  
      with self.test_session(use_gpu=True):
        tf_image1 = constant_op.constant(image1, shape=image1.shape,
                                         dtype=dtypes.float32)
        tf_image2 = constant_op.constant(image2, shape=image2.shape,
                                         dtype=dtypes.float32)
        tf_psnr = image_ops.psnr(tf_image1, tf_image2, 1.0, "psnr").eval()
        self.assertAllClose(psnr, tf_psnr, atol=0.001)
  
    def testPSNRMultiImage(self):
      image1 = self._RandomImage((10, 8, 8, 1), 1)
      image2 = self._RandomImage((10, 8, 8, 1), 1)
      psnr = self._PSNR_NumPy(image1, image2, 1)
  
      with self.test_session(use_gpu=True):
        tf_image1 = constant_op.constant(image1, shape=image1.shape,
                                         dtype=dtypes.float32)
        tf_image2 = constant_op.constant(image2, shape=image2.shape,
                                         dtype=dtypes.float32)
        tf_psnr = image_ops.psnr(tf_image1, tf_image2, 1, "psnr").eval()
        self.assertAllClose(psnr, tf_psnr, atol=0.001)
  
    def testGoldenPSNR(self):
      q20, q72, q95 = self._LoadTestImages()
  
      # Verify NumPy implementation first.
      # Golden values are generated using GNU Octave's psnr() function.
      psnr1 = self._PSNR_NumPy(q20, q72, 1)
      self.assertNear(30.321, psnr1, 0.001, msg="q20.dtype=" + str(q20.dtype))
      psnr2 = self._PSNR_NumPy(q20, q95, 1)
      self.assertNear(29.994, psnr2, 0.001)
      psnr3 = self._PSNR_NumPy(q72, q95, 1)
      self.assertNear(35.302, psnr3, 0.001)
  
      # Test TensorFlow implementation.
      with self.test_session(use_gpu=True):
        tf_q20 = constant_op.constant(q20, shape=q20.shape, dtype=dtypes.float32)
        tf_q72 = constant_op.constant(q72, shape=q72.shape, dtype=dtypes.float32)
        tf_q95 = constant_op.constant(q95, shape=q95.shape, dtype=dtypes.float32)
        tf_psnr1 = image_ops.psnr(tf_q20, tf_q72, 1, "psnr1").eval()
        tf_psnr2 = image_ops.psnr(tf_q20, tf_q95, 1, "psnr2").eval()
        tf_psnr3 = image_ops.psnr(tf_q72, tf_q95, 1, "psnr3").eval()
        self.assertAllClose(psnr1, tf_psnr1, atol=0.001)
        self.assertAllClose(psnr2, tf_psnr2, atol=0.001)
        self.assertAllClose(psnr3, tf_psnr3, atol=0.001)
  
    def testInfinity(self):
      q20, _, _ = self._LoadTestImages()
      psnr = self._PSNR_NumPy(q20, q20, 1)
      with self.test_session(use_gpu=True):
        tf_q20 = constant_op.constant(q20, shape=q20.shape, dtype=dtypes.float32)
        tf_psnr = image_ops.psnr(tf_q20, tf_q20, 1, "psnr").eval()
        self.assertAllClose(psnr, tf_psnr, atol=0.001)
  
    def testInt(self):
      img1 = self._RandomImage((10, 8, 8, 1), 255)
      img2 = self._RandomImage((10, 8, 8, 1), 255)
      img1 = constant_op.constant(img1, dtypes.uint8)
      img2 = constant_op.constant(img2, dtypes.uint8)
      psnr_uint8 = image_ops.psnr(img1, img2, 255)
      img1 = image_ops.convert_image_dtype(img1, dtypes.float32)
      img2 = image_ops.convert_image_dtype(img2, dtypes.float32)
      psnr_float32 = image_ops.psnr(img1, img2, 1.0)
      with self.test_session(use_gpu=True):
        self.assertAllClose(psnr_uint8.eval(), psnr_float32.eval(), atol=0.001)
  
  
  class SSIMTest(test_util.TensorFlowTestCase):
    """Tests for SSIM."""
  
    _filenames = ["checkerboard1.png",
                  "checkerboard2.png",
                  "checkerboard3.png",]
  
    _ssim = np.asarray([[1.000000, 0.230880, 0.231153],
                        [0.230880, 1.000000, 0.996828],
                        [0.231153, 0.996828, 1.000000]])
  
    def _LoadTestImage(self, sess, filename):
      content = io_ops.read_file(os.path.join(
          "tensorflow/core/lib/ssim/testdata", filename))
      im = image_ops.decode_png(content)
      im = image_ops.convert_image_dtype(im, dtypes.float32)
      im, = sess.run([im])
      return np.expand_dims(im, axis=0)
  
    def _LoadTestImages(self):
      with self.test_session(use_gpu=True) as sess:
        return [self._LoadTestImage(sess, f) for f in self._filenames]
  
    def _RandomImage(self, shape, max_val):
      """Returns an image or image batch with given shape."""
      return np.random.rand(*shape).astype(np.float32) * max_val
  
    def testAgainstMatlab(self):
      """Tests against values produced by Matlab."""
      img = self._LoadTestImages()
      expected = self._ssim[np.triu_indices(3)]
  
      ph = [array_ops.placeholder(dtype=dtypes.float32) for _ in range(2)]
      ssim = image_ops.ssim(*ph, max_val=1.0)
      with self.test_session(use_gpu=True):
        scores = [ssim.eval(dict(zip(ph, t)))
                  for t in itertools.combinations_with_replacement(img, 2)]
      self.assertAllClose(expected, np.squeeze(scores), atol=1e-4)
  
    def testBatch(self):
      img = self._LoadTestImages()
      expected = self._ssim[np.triu_indices(3, k=1)]
  
      img1, img2 = zip(*itertools.combinations(img, 2))
      img1 = np.concatenate(img1)
      img2 = np.concatenate(img2)
  
      ssim = image_ops.ssim(constant_op.constant(img1),
                            constant_op.constant(img2), 1.0)
      with self.test_session(use_gpu=True):
        self.assertAllClose(expected, ssim.eval(), atol=1e-4)
  
    def testBroadcast(self):
      img = self._LoadTestImages()[:2]
      expected = self._ssim[:2, :2]
  
      img = constant_op.constant(np.concatenate(img))
      img1 = array_ops.expand_dims(img, axis=0)  # batch dims: 1, 2.
      img2 = array_ops.expand_dims(img, axis=1)  # batch dims: 2, 1.
  
      ssim = image_ops.ssim(img1, img2, 1.0)
      with self.test_session(use_gpu=True):
        self.assertAllClose(expected, ssim.eval(), atol=1e-4)
  
    def testNegative(self):
      """Tests against negative SSIM index."""
      step = np.expand_dims(np.arange(0, 256, 16, dtype=np.uint8), axis=0)
      img1 = np.tile(step, (16, 1))
      img2 = np.fliplr(img1)
  
      img1 = img1.reshape((1, 16, 16, 1))
      img2 = img2.reshape((1, 16, 16, 1))
  
      ssim = image_ops.ssim(constant_op.constant(img1),
                            constant_op.constant(img2), 255)
      with self.test_session(use_gpu=True):
        self.assertLess(ssim.eval(), 0)
  
    def testInt(self):
      img1 = self._RandomImage((1, 16, 16, 3), 255)
      img2 = self._RandomImage((1, 16, 16, 3), 255)
      img1 = constant_op.constant(img1, dtypes.uint8)
      img2 = constant_op.constant(img2, dtypes.uint8)
      ssim_uint8 = image_ops.ssim(img1, img2, 255)
      img1 = image_ops.convert_image_dtype(img1, dtypes.float32)
      img2 = image_ops.convert_image_dtype(img2, dtypes.float32)
      ssim_float32 = image_ops.ssim(img1, img2, 1.0)
      with self.test_session(use_gpu=True):
        self.assertAllClose(ssim_uint8.eval(), ssim_float32.eval(), atol=0.001)
  
  
  class MultiscaleSSIMTest(test_util.TensorFlowTestCase):
    """Tests for MS-SSIM."""
  
    _filenames = ["checkerboard1.png",
                  "checkerboard2.png",
                  "checkerboard3.png",]
  
    _msssim = np.asarray([[1.000000, 0.091016, 0.091025],
                          [0.091016, 1.000000, 0.999567],
                          [0.091025, 0.999567, 1.000000]])
  
    def _LoadTestImage(self, sess, filename):
      content = io_ops.read_file(os.path.join(
          "tensorflow/core/lib/ssim/testdata", filename))
      im = image_ops.decode_png(content)
      im = image_ops.convert_image_dtype(im, dtypes.float32)
      im, = sess.run([im])
      return np.expand_dims(im, axis=0)
  
    def _LoadTestImages(self):
      with self.test_session(use_gpu=True) as sess:
        return [self._LoadTestImage(sess, f) for f in self._filenames]
  
    def _RandomImage(self, shape, max_val):
      """Returns an image or image batch with given shape."""
      return np.random.rand(*shape).astype(np.float32) * max_val
  
    def testAgainstMatlab(self):
      """Tests against MS-SSIM computed with Matlab implementation.
  
      For color images, MS-SSIM scores are averaged over color channels.
      """
      img = self._LoadTestImages()
      expected = self._msssim[np.triu_indices(3)]
  
      ph = [array_ops.placeholder(dtype=dtypes.float32) for _ in range(2)]
      msssim = image_ops.ssim_multiscale(*ph, max_val=1.0)
      with self.test_session(use_gpu=True):
        scores = [msssim.eval(dict(zip(ph, t)))
                  for t in itertools.combinations_with_replacement(img, 2)]
  
      self.assertAllClose(expected, np.squeeze(scores), atol=1e-4)
  
    def testUnweightedIsDifferentiable(self):
      img = self._LoadTestImages()
      ph = [array_ops.placeholder(dtype=dtypes.float32) for _ in range(2)]
      scalar = constant_op.constant(1.0, dtype=dtypes.float32)
      scaled_ph = [x * scalar for x in ph]
      msssim = image_ops.ssim_multiscale(*scaled_ph, max_val=1.0,
                                         power_factors=(1, 1, 1, 1, 1))
      grads = gradients.gradients(msssim, scalar)
      with self.test_session(use_gpu=True) as sess:
        np_grads = sess.run(grads, feed_dict={ph[0]: img[0], ph[1]: img[1]})
      self.assertTrue(np.isfinite(np_grads).all())
  
    def testBatch(self):
      """Tests MS-SSIM computed in batch."""
      img = self._LoadTestImages()
      expected = self._msssim[np.triu_indices(3, k=1)]
  
      img1, img2 = zip(*itertools.combinations(img, 2))
      img1 = np.concatenate(img1)
      img2 = np.concatenate(img2)
  
      msssim = image_ops.ssim_multiscale(constant_op.constant(img1),
                                         constant_op.constant(img2), 1.0)
      with self.test_session(use_gpu=True):
        self.assertAllClose(expected, msssim.eval(), 1e-4)
  
    def testBroadcast(self):
      """Tests MS-SSIM broadcasting."""
      img = self._LoadTestImages()[:2]
      expected = self._msssim[:2, :2]
  
      img = constant_op.constant(np.concatenate(img))
      img1 = array_ops.expand_dims(img, axis=0)  # batch dims: 1, 2.
      img2 = array_ops.expand_dims(img, axis=1)  # batch dims: 2, 1.
  
      score_tensor = image_ops.ssim_multiscale(img1, img2, 1.0)
      with self.test_session(use_gpu=True):
        self.assertAllClose(expected, score_tensor.eval(), 1e-4)
  
    def testRange(self):
      """Tests against low MS-SSIM score.
  
      MS-SSIM is a geometric mean of SSIM and CS scores of various scales.
      If any of the value is negative so that the geometric mean is not
      well-defined, then treat the MS-SSIM score as zero.
      """
      with self.test_session(use_gpu=True) as sess:
        img1 = self._LoadTestImage(sess, "checkerboard1.png")
        img2 = self._LoadTestImage(sess, "checkerboard3.png")
        images = [img1, img2, np.zeros_like(img1),
                  np.full_like(img1, fill_value=255)]
  
        images = [ops.convert_to_tensor(x, dtype=dtypes.float32) for x in images]
        msssim_ops = [image_ops.ssim_multiscale(x, y, 1.0)
                      for x, y in itertools.combinations(images, 2)]
        msssim = sess.run(msssim_ops)
        msssim = np.squeeze(msssim)
  
      self.assertTrue(np.all(msssim >= 0.0))
      self.assertTrue(np.all(msssim <= 1.0))
  
    def testInt(self):
      img1 = self._RandomImage((1, 180, 240, 3), 255)
      img2 = self._RandomImage((1, 180, 240, 3), 255)
      img1 = constant_op.constant(img1, dtypes.uint8)
      img2 = constant_op.constant(img2, dtypes.uint8)
      ssim_uint8 = image_ops.ssim_multiscale(img1, img2, 255)
      img1 = image_ops.convert_image_dtype(img1, dtypes.float32)
      img2 = image_ops.convert_image_dtype(img2, dtypes.float32)
      ssim_float32 = image_ops.ssim_multiscale(img1, img2, 1.0)
      with self.test_session(use_gpu=True):
        self.assertAllClose(ssim_uint8.eval(), ssim_float32.eval(), atol=0.001)
  
  
  class ImageGradientsTest(test_util.TensorFlowTestCase):
  
    def testImageGradients(self):
      shape = [1, 2, 4, 1]
      img = constant_op.constant([[1, 3, 4, 2], [8, 7, 5, 6]])
      img = array_ops.reshape(img, shape)
  
      expected_dy = np.reshape([[7, 4, 1, 4], [0, 0, 0, 0]], shape)
      expected_dx = np.reshape([[2, 1, -2, 0], [-1, -2, 1, 0]], shape)
  
      dy, dx = image_ops.image_gradients(img)
      with self.cached_session():
        actual_dy = dy.eval()
        actual_dx = dx.eval()
        self.assertAllClose(expected_dy, actual_dy)
        self.assertAllClose(expected_dx, actual_dx)
  
    def testImageGradientsMultiChannelBatch(self):
      batch = [[[[1, 2], [2, 5], [3, 3]],
                [[8, 4], [5, 1], [9, 8]]],
               [[[5, 3], [7, 9], [1, 6]],
                [[1, 2], [6, 3], [6, 3]]]]
  
      expected_dy = [[[[7, 2], [3, -4], [6, 5]],
                      [[0, 0], [0, 0], [0, 0]]],
                     [[[-4, -1], [-1, -6], [5, -3]],
                      [[0, 0], [0, 0], [0, 0]]]]
  
      expected_dx = [[[[1, 3], [1, -2], [0, 0]],
                      [[-3, -3], [4, 7], [0, 0]]],
                     [[[2, 6], [-6, -3], [0, 0]],
                      [[5, 1], [0, 0], [0, 0]]]]
  
      batch = constant_op.constant(batch)
      assert batch.get_shape().as_list() == [2, 2, 3, 2]
      dy, dx = image_ops.image_gradients(batch)
      with self.test_session(use_gpu=True):
        actual_dy = dy.eval()
        actual_dx = dx.eval()
        self.assertAllClose(expected_dy, actual_dy)
        self.assertAllClose(expected_dx, actual_dx)
  
    def testImageGradientsBadShape(self):
      # [2 x 4] image but missing batch and depth dimensions.
      img = constant_op.constant([[1, 3, 4, 2], [8, 7, 5, 6]])
      with self.assertRaises(ValueError):
        image_ops.image_gradients(img)
  
  
  class SobelEdgesTest(test_util.TensorFlowTestCase):
  
    def testSobelEdges1x2x3x1(self):
      img = constant_op.constant([[1, 3, 6], [4, 1, 5]],
                                 dtype=dtypes.float32, shape=[1, 2, 3, 1])
      expected = np.reshape([[[0, 0], [0, 12], [0, 0]],
                             [[0, 0], [0, 12], [0, 0]]], [1, 2, 3, 1, 2])
      sobel = image_ops.sobel_edges(img)
      with self.test_session(use_gpu=True):
        actual_sobel = sobel.eval()
        self.assertAllClose(expected, actual_sobel)
  
    def testSobelEdges5x3x4x2(self):
      batch_size = 5
      plane = np.reshape([[1, 3, 6, 2], [4, 1, 5, 7], [2, 5, 1, 4]],
                         [1, 3, 4, 1])
      two_channel = np.concatenate([plane, plane], axis=3)
      batch = np.concatenate([two_channel] * batch_size, axis=0)
      img = constant_op.constant(batch, dtype=dtypes.float32,
                                 shape=[batch_size, 3, 4, 2])
  
      expected_plane = np.reshape([[[0, 0], [0, 12], [0, 10], [0, 0]],
                                   [[6, 0], [0, 6], [-6, 10], [-6, 0]],
                                   [[0, 0], [0, 0], [0, 10], [0, 0]]],
                                  [1, 3, 4, 1, 2])
      expected_two_channel = np.concatenate(
          [expected_plane, expected_plane], axis=3)
      expected_batch = np.concatenate([expected_two_channel] * batch_size, axis=0)
  
      sobel = image_ops.sobel_edges(img)
      with self.test_session(use_gpu=True):
        actual_sobel = sobel.eval()
        self.assertAllClose(expected_batch, actual_sobel)
  
  
  class DecodeImageTest(test_util.TensorFlowTestCase):
  
    def testJpegUint16(self):
      with self.test_session(use_gpu=True) as sess:
        base = "tensorflow/core/lib/jpeg/testdata"
        jpeg0 = io_ops.read_file(os.path.join(base, "jpeg_merge_test1.jpg"))
        image0 = image_ops.decode_image(jpeg0, dtype=dtypes.uint16)
        image1 = image_ops.convert_image_dtype(image_ops.decode_jpeg(jpeg0),
                                               dtypes.uint16)
        image0, image1 = sess.run([image0, image1])
        self.assertAllEqual(image0, image1)
  
    def testPngUint16(self):
      with self.test_session(use_gpu=True) as sess:
        base = "tensorflow/core/lib/png/testdata"
        png0 = io_ops.read_file(os.path.join(base, "lena_rgba.png"))
        image0 = image_ops.decode_image(png0, dtype=dtypes.uint16)
        image1 = image_ops.convert_image_dtype(
            image_ops.decode_png(png0, dtype=dtypes.uint16), dtypes.uint16)
        image0, image1 = sess.run([image0, image1])
        self.assertAllEqual(image0, image1)
  
    def testGifUint16(self):
      with self.test_session(use_gpu=True) as sess:
        base = "tensorflow/core/lib/gif/testdata"
        gif0 = io_ops.read_file(os.path.join(base, "scan.gif"))
        image0 = image_ops.decode_image(gif0, dtype=dtypes.uint16)
        image1 = image_ops.convert_image_dtype(image_ops.decode_gif(gif0),
                                               dtypes.uint16)
        image0, image1 = sess.run([image0, image1])
        self.assertAllEqual(image0, image1)
  
    def testBmpUint16(self):
      with self.test_session(use_gpu=True) as sess:
        base = "tensorflow/core/lib/bmp/testdata"
        bmp0 = io_ops.read_file(os.path.join(base, "lena.bmp"))
        image0 = image_ops.decode_image(bmp0, dtype=dtypes.uint16)
        image1 = image_ops.convert_image_dtype(image_ops.decode_bmp(bmp0),
                                               dtypes.uint16)
        image0, image1 = sess.run([image0, image1])
        self.assertAllEqual(image0, image1)
  
    def testJpegFloat32(self):
      with self.test_session(use_gpu=True) as sess:
        base = "tensorflow/core/lib/jpeg/testdata"
        jpeg0 = io_ops.read_file(os.path.join(base, "jpeg_merge_test1.jpg"))
        image0 = image_ops.decode_image(jpeg0, dtype=dtypes.float32)
        image1 = image_ops.convert_image_dtype(image_ops.decode_jpeg(jpeg0),
                                               dtypes.float32)
        image0, image1 = sess.run([image0, image1])
        self.assertAllEqual(image0, image1)
  
    def testPngFloat32(self):
      with self.test_session(use_gpu=True) as sess:
        base = "tensorflow/core/lib/png/testdata"
        png0 = io_ops.read_file(os.path.join(base, "lena_rgba.png"))
        image0 = image_ops.decode_image(png0, dtype=dtypes.float32)
        image1 = image_ops.convert_image_dtype(
            image_ops.decode_png(png0, dtype=dtypes.uint16), dtypes.float32)
        image0, image1 = sess.run([image0, image1])
        self.assertAllEqual(image0, image1)
  
    def testGifFloat32(self):
      with self.test_session(use_gpu=True) as sess:
        base = "tensorflow/core/lib/gif/testdata"
        gif0 = io_ops.read_file(os.path.join(base, "scan.gif"))
        image0 = image_ops.decode_image(gif0, dtype=dtypes.float32)
        image1 = image_ops.convert_image_dtype(image_ops.decode_gif(gif0),
                                               dtypes.float32)
        image0, image1 = sess.run([image0, image1])
        self.assertAllEqual(image0, image1)
  
    def testBmpFloat32(self):
      with self.test_session(use_gpu=True) as sess:
        base = "tensorflow/core/lib/bmp/testdata"
        bmp0 = io_ops.read_file(os.path.join(base, "lena.bmp"))
        image0 = image_ops.decode_image(bmp0, dtype=dtypes.float32)
        image1 = image_ops.convert_image_dtype(image_ops.decode_bmp(bmp0),
                                               dtypes.float32)
        image0, image1 = sess.run([image0, image1])
        self.assertAllEqual(image0, image1)
  
  
  if __name__ == "__main__":
    googletest.main()
   # Copyright 2017 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 Momentum."""
  
  from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  
  import numpy as np
  
  from tensorflow.compiler.tests import xla_test
  from tensorflow.python.framework import constant_op
  from tensorflow.python.framework import dtypes
  from tensorflow.python.ops import array_ops
  from tensorflow.python.ops import resource_variable_ops
  from tensorflow.python.ops import variables
  from tensorflow.python.platform import test
  from tensorflow.python.training import momentum as momentum_lib
  
  
  class MomentumOptimizerTest(xla_test.XLATestCase):
  
    def _update_nesterov_momentum_numpy(self, var, accum, g, lr, momentum):
      var += accum * lr * momentum
      accum = accum * momentum + g
      var -= lr * accum
      var -= accum * lr * momentum
      return var, accum
  
    def testBasic(self):
      for dtype in self.float_types:
        with self.cached_session(), self.test_scope():
          var0 = resource_variable_ops.ResourceVariable([1.0, 2.0], dtype=dtype)
          var1 = resource_variable_ops.ResourceVariable([3.0, 4.0], dtype=dtype)
          grads0 = constant_op.constant([0.1, 0.1], dtype=dtype)
          grads1 = constant_op.constant([0.01, 0.01], dtype=dtype)
          mom_opt = momentum_lib.MomentumOptimizer(
              learning_rate=2.0, momentum=0.9)
          mom_update = mom_opt.apply_gradients(
              zip([grads0, grads1], [var0, var1]))
          variables.global_variables_initializer().run()
          # Check we have slots
          self.assertEqual(["momentum"], mom_opt.get_slot_names())
          slot0 = mom_opt.get_slot(var0, "momentum")
          self.assertEquals(slot0.get_shape(), var0.get_shape())
          self.assertFalse(slot0 in variables.trainable_variables())
          slot1 = mom_opt.get_slot(var1, "momentum")
          self.assertEquals(slot1.get_shape(), var1.get_shape())
          self.assertFalse(slot1 in variables.trainable_variables())
  
          # Fetch params to validate initial values
          self.assertAllClose([1.0, 2.0], var0.eval())
          self.assertAllClose([3.0, 4.0], var1.eval())
          # Step 1: the momentum accumulators where 0. So we should see a normal
          # update: v -= grad * learning_rate
          mom_update.run()
          # Check that the momentum accumulators have been updated.
          self.assertAllCloseAccordingToType(np.array([0.1, 0.1]), slot0.eval())
          self.assertAllCloseAccordingToType(np.array([0.01, 0.01]), slot1.eval())
          # Check that the parameters have been updated.
          self.assertAllCloseAccordingToType(
              np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), var0.eval())
          self.assertAllCloseAccordingToType(
              np.array([3.0 - (0.01 * 2.0), 4.0 - (0.01 * 2.0)]), var1.eval())
          # Step 2: the momentum accumulators contain the previous update.
          mom_update.run()
          # Check that the momentum accumulators have been updated.
          self.assertAllCloseAccordingToType(
              np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), slot0.eval())
          self.assertAllCloseAccordingToType(
              np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), slot1.eval())
          # Check that the parameters have been updated.
          self.assertAllCloseAccordingToType(
              np.array([
                  1.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0),
                  2.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0)
              ]), var0.eval())
          self.assertAllCloseAccordingToType(
              np.array([
                  2.98 - ((0.9 * 0.01 + 0.01) * 2.0), 3.98 - (
                      (0.9 * 0.01 + 0.01) * 2.0)
              ]), var1.eval())
  
    def testNesterovMomentum(self):
      for dtype in self.float_types:
        with self.cached_session(), self.test_scope():
          var0 = resource_variable_ops.ResourceVariable([0.1, 0.2], dtype=dtype)
          var1 = resource_variable_ops.ResourceVariable([0.3, 0.4], dtype=dtype)
          var0_np = np.array([0.1, 0.2], dtype=dtype)
          var1_np = np.array([0.3, 0.4], dtype=dtype)
          accum0_np = np.array([0.0, 0.0], dtype=dtype)
          accum1_np = np.array([0.0, 0.0], dtype=dtype)
          cost = 0.4 * var0 * var0 + 0.9 * var1
          global_step = resource_variable_ops.ResourceVariable(
              array_ops.zeros([], dtypes.int32), name="global_step")
          mom_op = momentum_lib.MomentumOptimizer(
              learning_rate=0.1, momentum=0.9, use_nesterov=True)
          opt_op = mom_op.minimize(cost, global_step, [var0, var1])
          variables.global_variables_initializer().run()
          for _ in range(1, 5):
            opt_op.run()
            var0_np, accum0_np = self._update_nesterov_momentum_numpy(
                var0_np, accum0_np, var0_np * 0.8, 0.1, 0.9)
            var1_np, accum1_np = self._update_nesterov_momentum_numpy(
                var1_np, accum1_np, 0.9, 0.1, 0.9)
            self.assertAllCloseAccordingToType(var0_np, var0.eval())
            self.assertAllCloseAccordingToType(var1_np, var1.eval())
  
    def testTensorLearningRateAndMomentum(self):
      for dtype in self.float_types:
        with self.cached_session(), self.test_scope():
          var0 = resource_variable_ops.ResourceVariable([1.0, 2.0], dtype=dtype)
          var1 = resource_variable_ops.ResourceVariable([3.0, 4.0], dtype=dtype)
          grads0 = constant_op.constant([0.1, 0.1], dtype=dtype)
          grads1 = constant_op.constant([0.01, 0.01], dtype=dtype)
          mom_opt = momentum_lib.MomentumOptimizer(
              learning_rate=constant_op.constant(2.0),
              momentum=constant_op.constant(0.9))
          mom_update = mom_opt.apply_gradients(
              zip([grads0, grads1], [var0, var1]))
          variables.global_variables_initializer().run()
          # Check we have slots
          self.assertEqual(["momentum"], mom_opt.get_slot_names())
          slot0 = mom_opt.get_slot(var0, "momentum")
          self.assertEquals(slot0.get_shape(), var0.get_shape())
          self.assertFalse(slot0 in variables.trainable_variables())
          slot1 = mom_opt.get_slot(var1, "momentum")
          self.assertEquals(slot1.get_shape(), var1.get_shape())
          self.assertFalse(slot1 in variables.trainable_variables())
  
          # Fetch params to validate initial values
          self.assertAllClose([1.0, 2.0], var0.eval())
          self.assertAllClose([3.0, 4.0], var1.eval())
          # Step 1: the momentum accumulators where 0. So we should see a normal
          # update: v -= grad * learning_rate
          mom_update.run()
          # Check that the momentum accumulators have been updated.
          self.assertAllCloseAccordingToType(np.array([0.1, 0.1]), slot0.eval())
          self.assertAllCloseAccordingToType(np.array([0.01, 0.01]), slot1.eval())
          # Check that the parameters have been updated.
          self.assertAllCloseAccordingToType(
              np.array([1.0 - (0.1 * 2.0), 2.0 - (0.1 * 2.0)]), var0.eval())
          self.assertAllCloseAccordingToType(
              np.array([3.0 - (0.01 * 2.0), 4.0 - (0.01 * 2.0)]), var1.eval())
          # Step 2: the momentum accumulators contain the previous update.
          mom_update.run()
          # Check that the momentum accumulators have been updated.
          self.assertAllCloseAccordingToType(
              np.array([(0.9 * 0.1 + 0.1), (0.9 * 0.1 + 0.1)]), slot0.eval())
          self.assertAllCloseAccordingToType(
              np.array([(0.9 * 0.01 + 0.01), (0.9 * 0.01 + 0.01)]), slot1.eval())
          # Check that the parameters have been updated.
          self.assertAllCloseAccordingToType(
              np.array([
                  1.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0),
                  2.0 - (0.1 * 2.0) - ((0.9 * 0.1 + 0.1) * 2.0)
              ]), var0.eval())
          self.assertAllCloseAccordingToType(
              np.array([
                  2.98 - ((0.9 * 0.01 + 0.01) * 2.0), 3.98 - (
                      (0.9 * 0.01 + 0.01) * 2.0)
              ]), var1.eval())
  
  
  if __name__ == "__main__":
    test.main()
  
apis: tensorflow.python.ops.nn_ops.dropout tensorflow.python.ops.math_ops.mod tensorflow.python.ops.rnn_cell_impl.assert_like_rnncell tensorflow.python.ops.math_ops.tanh tensorflow.python.platform.tf_logging.warn tensorflow.python.ops.variable_scope.variable_scope tensorflow.python.util.nest.flatten tensorflow.python.ops.nn_ops.bias_add tensorflow.python.util.nest.is_sequence tensorflow.python.ops.array_ops.expand_dims tensorflow.python.framework.tensor_shape.TensorShape tensorflow.python.util.nest.assert_same_structure tensorflow.contrib.layers.python.layers.layers.layer_norm tensorflow.python.framework.constant_op.constant tensorflow.python.ops.init_ops.random_uniform_initializer tensorflow.python.framework.ops.add_to_collection tensorflow.python.ops.variable_scope.get_variable tensorflow.python.ops.math_ops.matmul tensorflow.python.ops.array_ops.slice tensorflow.python.ops.init_ops.zeros_initializer tensorflow.python.ops.gen_array_ops.tile tensorflow.python.ops.init_ops.constant_initializer tensorflow.python.ops.nn_ops.xw_plus_b tensorflow.python.ops.array_ops.zeros tensorflow.python.ops.array_ops.shape tensorflow.python.ops.nn_impl.l2_normalize tensorflow.python.framework.ops.control_dependencies tensorflow.python.ops.math_ops.cast tensorflow.python.ops.array_ops.constant tensorflow.python.ops.clip_ops.clip_by_value tensorflow.python.ops.variable_scope.get_variable_scope tensorflow.python.layers.base.InputSpec tensorflow.python.ops.math_ops.sigmoid tensorflow.python.ops.math_ops.log tensorflow.python.framework.ops.get_collection tensorflow.python.ops.array_ops.split tensorflow.python.ops.array_ops.concat tensorflow.python.ops.rnn_cell_impl.LSTMStateTuple tensorflow.python.ops.array_ops.reshape tensorflow.python.ops.array_ops.where tensorflow.python.framework.op_def_registry.get_registered_ops tensorflow.python.util.nest.map_structure tensorflow.contrib.compiler.jit.experimental_jit_scope
apis: numpy.ones tensorflow.python.ops.image_ops.psnr tensorflow.python.platform.test.is_gpu_available numpy.asarray numpy.dstack tensorflow.python.ops.image_ops.non_max_suppression tensorflow.python.ops.image_ops.convert_image_dtype tensorflow.python.ops.io_ops.read_file numpy.abs tensorflow.python.ops.image_ops.image_gradients tensorflow.python.ops.image_ops.decode_gif tensorflow.python.ops.gen_image_ops.adjust_saturation tensorflow.python.ops.gradients.gradients numpy.mean tensorflow.python.ops.array_ops.unstack tensorflow.python.framework.ops.device tensorflow.python.ops.array_ops.shape tensorflow.python.ops.image_ops.rgb_to_yiq tensorflow.python.framework.ops.control_dependencies tensorflow.python.ops.image_ops.yuv_to_rgb tensorflow.python.ops.image_ops.adjust_brightness tensorflow.python.ops.image_ops.decode_image tensorflow.python.framework.ops.convert_to_tensor numpy.square tensorflow.python.ops.image_ops.resize_image_with_crop_or_pad tensorflow.python.ops.image_ops.ssim_multiscale tensorflow.core.protobuf.config_pb2.ConfigProto numpy.rot90 tensorflow.python.ops.array_ops.strided_slice numpy.array tensorflow.python.ops.image_ops.adjust_gamma tensorflow.python.ops.image_ops.hsv_to_rgb numpy.random.seed tensorflow.python.ops.image_ops.crop_to_bounding_box tensorflow.python.ops.image_ops.adjust_hue tensorflow.python.ops.image_ops.rgb_to_yuv tensorflow.python.ops.image_ops.random_flip_left_right tensorflow.python.framework.constant_op.constant numpy.vstack tensorflow.python.ops.image_ops.sobel_edges numpy.reshape tensorflow.python.ops.image_ops.rgb_to_grayscale tensorflow.python.ops.image_ops.sample_distorted_bounding_box tensorflow.python.ops.image_ops.flip_left_right numpy.tile tensorflow.python.platform.test.gpu_device_name tensorflow.python.ops.image_ops.resize_bicubic numpy.triu_indices tensorflow.python.ops.image_ops.per_image_standardization tensorflow.python.ops.image_ops.decode_jpeg tensorflow.python.ops.image_ops.resize_area numpy.zeros_like numpy.sqrt numpy.concatenate tensorflow.python.ops.image_ops.resize_bilinear tensorflow.python.ops.image_ops.random_flip_up_down numpy.sum numpy.histogram tensorflow.python.ops.array_ops.expand_dims numpy.full_like tensorflow.python.ops.image_ops.decode_bmp tensorflow.python.ops.image_ops.adjust_contrast tensorflow.python.ops.math_ops.add numpy.fliplr tensorflow.python.ops.image_ops.resize_images tensorflow.python.ops.image_ops.grayscale_to_rgb tensorflow.python.client.session.Session numpy.random.rand numpy.isnan tensorflow.python.ops.control_flow_ops.group tensorflow.python.ops.random_ops.random_uniform numpy.random.uniform tensorflow.python.ops.image_ops.rot90 tensorflow.python.ops.image_ops.transpose_image tensorflow.python.ops.image_ops.decode_and_crop_jpeg numpy.zeros numpy.arange tensorflow.python.ops.image_ops.non_max_suppression_padded tensorflow.python.ops.image_ops.flip_up_down numpy.prod numpy.cumsum tensorflow.python.ops.image_ops.extract_jpeg_shape tensorflow.python.ops.array_ops.reshape tensorflow.python.ops.image_ops.yiq_to_rgb tensorflow.python.ops.image_ops.encode_jpeg numpy.isfinite tensorflow.python.ops.image_ops.adjust_saturation tensorflow.python.framework.ops.Graph tensorflow.python.ops.array_ops.placeholder tensorflow.python.ops.image_ops.encode_png tensorflow.python.ops.image_ops.total_variation numpy.ndarray numpy.expand_dims numpy.log10 tensorflow.python.ops.image_ops.pad_to_bounding_box tensorflow.python.ops.image_ops.resize_image_with_pad tensorflow.python.ops.image_ops.ssim tensorflow.python.ops.variables.global_variables_initializer tensorflow.python.ops.image_ops.decode_png numpy.all tensorflow.python.ops.random_ops.random_normal tensorflow.python.ops.image_ops.central_crop tensorflow.python.ops.image_ops.rgb_to_hsv numpy.empty numpy.squeeze tensorflow.python.platform.googletest.main tensorflow.python.ops.image_ops_impl._verify_compatible_image_shapes tensorflow.python.ops.array_ops.stack
apis: tensorflow.python.ops.array_ops.zeros tensorflow.python.ops.variables.global_variables_initializer tensorflow.python.ops.variables.trainable_variables tensorflow.python.platform.test.main tensorflow.python.ops.resource_variable_ops.ResourceVariable tensorflow.python.training.momentum.MomentumOptimizer numpy.array tensorflow.python.framework.constant_op.constant