<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 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)))