<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def softmax(input_, labels=None, name=PROVIDED, loss_weight=None, per_example_weights=None):
 """Applies softmax and if labels is not None, then it also adds a loss. Args: input_: A rank 2 Tensor or a Pretty Tensor holding the logits. labels: The target labels to learn as a float tensor. Use None to not include a training loss. name: The optional name. loss_weight: A scalar multiplier for the loss. per_example_weights: A Tensor with a weight per example. Returns: A tuple of the a handle to softmax and a handle to the loss tensor. Raises: ValueError: If the datatype is wrong. """

  if labels is not None:
    # Cache the current layer because we only want softmax to change the head.
    full = input_.as_layer()
    return SoftmaxResult(input_.softmax_activation(),
                         full.cross_entropy(
                             labels,
                             name=name,
                             loss_weight=loss_weight,
                             per_example_weights=per_example_weights))
  else:
    return SoftmaxResult(input_.softmax_activation(), None)