The optimized variant of Soft Nearest Neighbor Loss. Every time this
    tensor is evaluated, the temperature is optimized to minimize the loss
    value, this results in more numerically stable calculations of the SNNL.
    :param x: a matrix.
    :param y: a list of labels for each element of x.
    :param initial_temp: Temperature.
    :cos_distance: Boolean for using cosine or Euclidean distance.

    :returns: A tensor for the Soft Nearest Neighbor Loss of the points
              in x with labels y, optimized for temperature.

def optimized_temp_SNNL(x, y, initial_temp, cos_distance):
    """The optimized variant of Soft Nearest Neighbor Loss. Every time this
    tensor is evaluated, the temperature is optimized to minimize the loss
    value, this results in more numerically stable calculations of the SNNL.
    :param x: a matrix.
    :param y: a list of labels for each element of x.
    :param initial_temp: Temperature.
    :cos_distance: Boolean for using cosine or Euclidean distance.

    :returns: A tensor for the Soft Nearest Neighbor Loss of the points
              in x with labels y, optimized for temperature.
    """
    t = tf.Variable(1, dtype=tf.float32, trainable=False, name="temp")

    def inverse_temp(t):
      # pylint: disable=missing-docstring
      # we use inverse_temp because it was observed to be more stable when optimizing.
      return tf.div(initial_temp, t)

    ent_loss = SNNLCrossEntropy.SNNL(x, y, inverse_temp(t), cos_distance)
    updated_t = tf.assign(t, tf.subtract(t, 0.1*tf.gradients(ent_loss, t)[0]))
    inverse_t = inverse_temp(updated_t)
    return SNNLCrossEntropy.SNNL(x, y, inverse_t, cos_distance)