docs: def apply_channel(val: Any,
          args: ApplyChannelArgs,
          default: TDefault = RaiseTypeErrorIfNotProvided
  ) -> Union[np.ndarray, TDefault]:
      """High performance evolution under a channel evolution.
  
      If `val` defines an `_apply_channel_` method, that method will be
      used to apply `val`'s channel effect to the target tensor. Otherwise, if
      `val` defines an `_apply_unitary_` method, that method will be used to
      apply `val`s channel effect to the target tensor.  Otherwise, if `val`
      returns a non-default channel with `cirq.channel`, that channel will be
      applied using a generic method.  If none of these cases apply, an
      exception is raised or the specified default value is returned.
  
  
      Args:
          val: The value with a channel to apply to the target.
          args: A mutable `cirq.ApplyChannelArgs` object describing the target
              tensor, available workspace, and left and right axes to operate on.
              The attributes of this object will be mutated as part of computing
              the result.
          default: What should be returned if `val` doesn't have a channel. If
              not specified, a TypeError is raised instead of returning a default
              value.
  
      Returns:
          If the receiving object is not able to apply a channel,
          the specified default value is returned (or a TypeError is raised). If
          this occurs, then `target_tensor` should not have been mutated.
  
          If the receiving object was able to work inline, directly
          mutating `target_tensor` it will return `target_tensor`. The caller is
          responsible for checking if the result is `target_tensor`.
  
          If the receiving object wrote its output over `out_buffer`, the
          result will be `out_buffer`. The caller is responsible for
          checking if the result is `out_buffer` (and e.g. swapping
          the buffer for the target tensor before the next call).
  
          Note that it is an error for the return object to be either of the
          auxiliary buffers, and the method will raise an AssertionError if
          this contract is violated.
  
          The receiving object may also write its output over a new buffer
          that it created, in which case that new array is returned.
  
      Raises:
          TypeError: `val` doesn't have a channel and `default` wasn't specified.
          AssertionError: if the
      """
      # Check if the specialized method is present.
      func = getattr(val, '_apply_channel_', None)
      if func is not None:
          result = func(args)
          if result is not NotImplemented and result is not None:
              def err_str(buf_num_str):
                  return (
                      "Object of type '{}' returned a result object equal to "
                      "auxiliary_buffer{}. This type violates the contract "
                      "that appears in apply_unitary's documentation.".format(
                          type(val), buf_num_str))
              assert result is not args.auxiliary_buffer0, err_str('0')
              assert result is not args.auxiliary_buffer1, err_str('1')
              return result
  
      # Possibly use `apply_unitary`.
      result = _apply_unitary(val, args)
      if result is not None:
          return result
  
      # Fallback to using the object's `_channel_` matrices.
      krauss = channel(val, None)
      if krauss is not None:
          return _apply_krauss(krauss, args)
  
      # Don't know how to apply channel. Fallback to specified default behavior.
      if default is not RaiseTypeErrorIfNotProvided:
          return default
      raise TypeError(
              "object of type '{}' has no _apply_channel_, _apply_unitary_, "
              "_unitary_, or _channel_ methods (or they returned None or "
              "NotImplemented).".format(type(val)))