TensorPlay

Latest development documentation · Updated 2026-09-08. A documentation snapshot for package 1.0.0.dev20260909 is not available.

On this page

Source code for tensorplay.nn.functional

"""Functional interface."""

import math
import os
import warnings
from typing import Any, Optional

import tensorplay
import tensorplay._C as _C
from tensorplay._C import _add_docstr, DType
from tensorplay import Tensor
from tensorplay.graph import capture_call as _capture_call


[docs]
def threshold(
    input: Tensor,
    threshold: float,
    value: float,
    inplace: bool = False,
) -> Tensor:
    r"""Apply a threshold to each element of the input Tensor.

    See :class:`~tensorplay.nn.Threshold` for more details.
    """
    captured = _capture_call(
        globals()["threshold"], (input, threshold, value), {"inplace": inplace}
    )
    if captured is not None:
        return captured
    if inplace:
        result = _C.threshold_(input, threshold, value)
    else:
        result = _C.threshold(input, threshold, value)
    return result




[docs]
def silu(input: Tensor, inplace: bool = False) -> Tensor:
    r"""Apply the Sigmoid Linear Unit (SiLU) function, element-wise.

    The SiLU function is also known as the swish function.

    .. math::
        \text{silu}(x) = x * \sigma(x), \text{where } \sigma(x) \text{ is the logistic sigmoid.}

    .. note::
        See `Gaussian Error Linear Units (GELUs) <https://arxiv.org/abs/1606.08415>`_
        where the SiLU (Sigmoid Linear Unit) was originally coined, and see
        `Sigmoid-Weighted Linear Units for Neural Network Function Approximation
        in Reinforcement Learning <https://arxiv.org/abs/1702.03118>`_ and `Swish:
        a Self-Gated Activation Function <https://arxiv.org/abs/1710.05941v1>`_
        where the SiLU was experimented with later.

    See :class:`~tensorplay.nn.SiLU` for more details.
    """
    captured = _capture_call(silu, (input,), {"inplace": inplace})
    if captured is not None:
        return captured
    if inplace:
        return tensorplay._C.silu_(input)
    return tensorplay._C.silu(input)



gelu = _add_docstr(
    tensorplay._C.gelu,
    r"""
gelu(input, approximate = 'none') -> Tensor

When the approximate argument is 'none', it applies element-wise the function
:math:`\text{GELU}(x) = x * \Phi(x)`

where :math:`\Phi(x)` is the Cumulative Distribution Function for Gaussian Distribution.

When the approximate argument is 'tanh', Gelu is estimated with

.. math::
    \text{GELU}(x) = 0.5 * x * (1 + \text{Tanh}(\sqrt{2 / \pi} * (x + 0.044715 * x^3)))

See `Gaussian Error Linear Units (GELUs) <https://arxiv.org/abs/1606.08415>`_.
""",
)



[docs]
def linear(input: Tensor, weight: Tensor, bias: Optional[Tensor] = None) -> Tensor:
    r"""Applies a linear transformation to the incoming data: :math:`y = xA^T + b`.

    Shape:
        - Input: :math:`(*, H_\text{in})` where :math:`*` means any number of
          dimensions including none and :math:`H_\text{in} = \text{in\_features}`.
        - Weight: :math:`(H_\text{out}, H_\text{in})` where
          :math:`H_\text{out} = \text{out\_features}`.
        - Bias: :math:`(H_\text{out})`
        - Output: :math:`(*, H_\text{out})`

    See :class:`~tensorplay.nn.Linear` for more details.
    """
    captured = _capture_call(linear, (input, weight, bias), {})
    if captured is not None:
        return captured

    distributed = _distributed_tensor_types()
    if distributed is not None and any(
        isinstance(value, distributed[0]) for value in (input, weight, bias)
    ):
        return _distributed_linear(input, weight, bias, distributed[0])

    # _matmul_impl checks this again later, but the native flatten path does
    # not work on scalar inputs, so try to catch this here already
    input_dim = input.dim()
    weight_dim = weight.dim()
    if input_dim == 0 or weight_dim == 0:
        raise RuntimeError(
            "both arguments to linear need to be at least 1D, but they are "
            f"{input_dim}D and {weight_dim}D"
        )

    # Native dispatch: CPU runs linear_kernel (single seeded-GEMM addmm with
    # raw as_strided weight.t(), bias folded into the epilogue / seed);
    # other backends fall through to the recordable matmul/add composite.
    return tensorplay.linear(input, weight, bias)



def _distributed_tensor_types() -> tuple[type, type] | None:
    try:
        from tensorplay.distributed.tensor import DTensor
    except (ImportError, AttributeError):
        return None
    return DTensor, Tensor


def _distributed_linear(input: Any, weight: Any, bias: Any, dtensor_type: type) -> Any:
    from tensorplay.distributed.tensor import DTensor, Partial, Shard

    input_dtensor = input if isinstance(input, dtensor_type) else None
    weight_dtensor = weight if isinstance(weight, dtensor_type) else None
    bias_value = bias.to_local() if isinstance(bias, dtensor_type) else bias
    input_value = input.to_local() if input_dtensor is not None else input
    weight_value = weight.to_local() if weight_dtensor is not None else weight
    result = tensorplay.linear(input_value, weight_value, bias_value)

    template = input_dtensor or weight_dtensor
    if template is None:
        return result
    placements = list(template.placements)
    if weight_dtensor is not None:
        for index, placement in enumerate(weight_dtensor.placements):
            if isinstance(placement, Shard) and placement.dim == 0:
                placements[index] = Shard(-1)
            elif isinstance(placement, Shard) and placement.dim == 1:
                placements[index] = Partial("sum")
    elif input_dtensor is not None:
        for index, placement in enumerate(input_dtensor.placements):
            if isinstance(placement, Shard) and placement.dim == input_dtensor.ndim - 1:
                placements[index] = Partial("sum")
    shape = tuple(input_value.shape[:-1]) + (int(weight.shape[0]),)
    stride = [1] * len(shape)
    for index in range(len(shape) - 2, -1, -1):
        stride[index] = stride[index + 1] * int(shape[index + 1])
    return DTensor.from_local(
        result,
        template.device_mesh,
        placements,
        shape=shape,
        stride=tuple(stride),
        run_check=False,
    )



[docs]
def bilinear(input1, input2, weight, bias=None):
    if list(input1.shape)[:-1] != list(input2.shape)[:-1]:
        raise ValueError("input1 and input2 must have the same batch dimensions")
    
    out_features, in1_features, in2_features = weight.shape
    
    # w: (Out, H1, H2) -> (Out, H2, H1)
    # TensorPlay permute expects a sequence
    w = weight.permute([0, 2, 1])
    
    # w: (Out * H2, H1)
    w = w.reshape(-1, in1_features)
    
    # input1: (*, H1)
    # input1 @ w.T: (*, H1) @ (H1, Out * H2) -> (*, Out * H2)
    temp = input1.matmul(w.t())
    
    # temp: (*, Out * H2)
    # Reshape to (*, Out, H2)
    new_shape = list(input1.shape)[:-1] + [out_features, in2_features]
    temp = temp.view(new_shape)
    
    # input2: (*, H2)
    # unsqueeze to (*, H2, 1)
    input2_expanded = input2.unsqueeze(-1)
    
    # temp: (*, Out, H2)
    # result: (*, Out, 1)
    output = temp.matmul(input2_expanded)
    
    # squeeze
    output = output.squeeze(-1)
    
    if bias is not None:
        output = output + bias
        
    return output



[docs]
def relu(input, inplace=False):
    captured = _capture_call(
        relu,
        (input,),
        {"inplace": True} if inplace else {},
    )
    if captured is not None:
        return captured
    if inplace:
        return _C.relu_(input)
    return _C.relu(input)



[docs]
def softmax(input, dim=None, dtype=None):
    if dim is None:
        dim = -1
    if dtype is None:
        dtype = tensorplay.undefined
    return input.softmax(dim, dtype)



[docs]
def log_softmax(input, dim=None, dtype=None):
    captured = _capture_call(log_softmax, (input, dim, dtype), {})
    if captured is not None:
        return captured
    if dim is None:
        dim = -1
    if dtype is None:
        dtype = tensorplay.undefined
    return _C.log_softmax(input, dim, dtype)



[docs]
def prelu(input, weight):
    captured = _capture_call(prelu, (input, weight), {})
    if captured is not None:
        return captured
    # PReLU(x) = max(0, x) + weight * min(0, x)
    #          = relu(x) - weight * relu(-x)
    
    if weight.numel() != 1:
        if input.dim() < 2:
             raise ValueError("Input must have at least 2 dimensions when num_parameters > 1")
        
        # Check if num_parameters matches channel dim (dim 1)
        if input.size(1) != weight.numel():
            raise ValueError(f"num_parameters {weight.numel()} does not match input channel size {input.size(1)}")
        
        # Reshape weight for broadcasting
        # We want (1, C, 1, ...)
        view_shape = [1] * input.dim()
        view_shape[1] = weight.numel()
        weight = weight.view(view_shape)

    return _C.prelu(input, weight)



[docs]
def flatten(input, start_dim=0, end_dim=-1):
    return input.flatten(start_dim, end_dim)



[docs]
def embedding(input, weight, padding_idx=None, max_norm=None, norm_type=2.0, scale_grad_by_freq=False, sparse=False):
    captured = _capture_call(embedding, (input, weight, padding_idx, max_norm, norm_type, scale_grad_by_freq, sparse), {})
    if captured is not None:
        return captured
    try:
        from tensorplay.distributed.tensor import DTensor, Shard
    except (ImportError, AttributeError):
        DTensor = ()
        Shard = ()
    if DTensor and (isinstance(input, DTensor) or isinstance(weight, DTensor)):
        input_dtensor = input if isinstance(input, DTensor) else None
        weight_dtensor = weight if isinstance(weight, DTensor) else None
        input_value = input.to_local() if input_dtensor is not None else input
        weight_value = weight.to_local() if weight_dtensor is not None else weight
        if max_norm is not None:
            raise NotImplementedError("embedding: max_norm is not supported")
        if padding_idx is None:
            normalized_padding_idx = -1
        else:
            if padding_idx < -weight.size(0) or padding_idx >= weight.size(0):
                raise AssertionError("Padding_idx must be within num_embeddings")
            normalized_padding_idx = padding_idx + weight.size(0) if padding_idx < 0 else padding_idx
        result = _C.embedding(
            weight_value,
            input_value,
            normalized_padding_idx,
            scale_grad_by_freq,
            sparse,
        )
        template = weight_dtensor or input_dtensor
        placements = list(template.placements)
        if input_dtensor is not None and weight_dtensor is None:
            placements = list(input_dtensor.placements)
        shape = tuple(input.shape) + (int(weight.shape[1]),)
        stride = [1] * len(shape)
        for index in range(len(shape) - 2, -1, -1):
            stride[index] = stride[index + 1] * int(shape[index + 1])
        return DTensor.from_local(
            result,
            template.device_mesh,
            placements,
            shape=shape,
            stride=tuple(stride),
            run_check=False,
        )
    if max_norm is not None:
        _no_grad_embedding_renorm_(weight, input, max_norm, norm_type)
    if padding_idx is None:
        padding_idx = -1
    else:
        if padding_idx < -weight.size(0) or padding_idx >= weight.size(0):
            raise AssertionError('Padding_idx must be within num_embeddings')
        if padding_idx < 0:
            padding_idx += weight.size(0)
    return _C.embedding(weight, input, padding_idx, scale_grad_by_freq, sparse)


# Add more functionals as needed


[docs]
def dropout(input, p=0.5, training=True, inplace=False):
    captured = _capture_call(dropout, (input, p, training, inplace), {})
    if captured is not None:
        return captured
    if p < 0 or p > 1:
        raise ValueError("dropout probability has to be between 0 and 1, but got {}".format(p))
    if not training or p == 0:
        return input

    if p == 1:
        result = _C.zeros_like(input)
        return input.copy_(result) if inplace else result

    if inplace:
        # dropout_ mutates self and records the mask for backward).
        mask = (_C.rand(input.shape, device=input.device) > p).to(input.dtype)
        return input.mul_(mask).mul_(1.0 / (1.0 - p))

    out, _mask = _C.native_dropout(input, p)
    return out



[docs]
def dropout2d(input, p=0.5, training=True, inplace=False):
    if p < 0 or p > 1:
        raise ValueError("dropout probability has to be between 0 and 1, but got {}".format(p))
    if not training or p == 0:
        return input
        
    # Input must be at least 2D (N, C, ...)
    if input.dim() < 2:
         raise ValueError("Feature dropout requires at least 2 dimensions")

    if p == 1:
        result = _C.zeros_like(input)
        return input.copy_(result) if inplace else result
         
    shape = list(input.shape)
    shape[2:] = [1] * (input.dim() - 2)
    
    mask = (_C.rand(shape, device=input.device) > p).to(input.dtype)
    scale = 1.0 / (1.0 - p)
    
    if inplace:
        return input.mul_(mask).mul_(scale)
    else:
        return input * mask * scale



[docs]
def dropout3d(input, p=0.5, training=True, inplace=False):
    return dropout2d(input, p, training, inplace)



[docs]
def alpha_dropout(input, p=0.5, training=True, inplace=False):
    if p < 0 or p > 1:
        raise ValueError("dropout probability has to be between 0 and 1, but got {}".format(p))
    if not training or p == 0:
        return input

    # Native fused forward plus generated backward through the saved mask.
    if p == 1:
        result = _C.zeros_like(input)
    else:
        result, _mask = _C.native_alpha_dropout(input, p)
    if inplace:
        return input.copy_(result)
    return result




[docs]
def feature_dropout(input, p=0.5, training=False, inplace=False):
    r"""Randomly zeroes entire channels (dim 1)."""
    if p < 0 or p > 1:
        raise ValueError("dropout probability has to be between 0 and 1, but got {}".format(p))
    if input.dim() < 2:
        raise RuntimeError(
            f"Feature dropout requires at least 2 dimensions in the input, "
            f"but got {input.dim()}")
    if not training or p == 0 or input.numel() == 0:
        return input

    if p == 1:
        result = _C.zeros_like(input)
        return input.copy_(result) if inplace else result

    result, _mask = _C.native_feature_dropout(input, p)
    if inplace:
        return input.copy_(result)
    return result




[docs]
def dropout_(input, p=0.5, training=True):
    r"""In-place version of :func:`dropout`."""
    return dropout(input, p=p, training=training, inplace=True)




[docs]
def rrelu_(input, lower=1.0 / 8, upper=1.0 / 3, training=False):
    r"""In-place version of :func:`rrelu`."""
    return rrelu(input, lower=lower, upper=upper, training=training,
                 inplace=True)




[docs]
def feature_dropout_(input, p=0.5, training=True):
    r"""In-place version of :func:`feature_dropout`."""
    return feature_dropout(input, p=p, training=training, inplace=True)


# Pooling helpers
def _pair(x):
    if isinstance(x, (int, float)):
        return (x, x)
    return tuple(x)

def _single(x):
    if isinstance(x, (int, float)):
        return (x,)
    return tuple(x)

def _triple(x):
    if isinstance(x, (int, float)):
        return (x, x, x)
    return tuple(x)


[docs]
def conv1d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1):
    r"""Applies a 1D convolution over an input signal composed of several input planes.

    See :class:`~tensorplay.nn.Conv1d` for details and output shape.

    Args:
        input: input tensor of shape :math:`(\text{minibatch} , \text{in\_channels} , iW)`
        weight: filters of shape :math:`(\text{out\_channels} , \frac{\text{in\_channels}}{\text{groups}} , kW)`
        bias: optional bias of shape :math:`(\text{out\_channels})`. Default: ``None``
        stride: the stride of the convolving kernel. Can be a single number or
          a one-element tuple `(sW,)`. Default: 1
        padding: implicit paddings on both sides of the input. Can be a single number or a one-element tuple `(padW,)`. Default: 0
        dilation: the spacing between kernel elements. Can be a single number or
          a one-element tuple `(dW,)`. Default: 1
        groups: split input into groups, :math:`\text{in\_channels}` should be divisible by
          the number of groups. Default: 1

    Examples::

        >>> inputs = tp.randn(33, 16, 30)
        >>> filters = tp.randn(20, 16, 5)
        >>> F.conv1d(inputs, filters)
    """
    captured = _capture_call(conv1d, (input, weight, bias, stride, padding, dilation, groups), {})
    if captured is not None:
        return captured
    stride = _single(stride)
    padding = _single(padding)
    dilation = _single(dilation)
    if bias is None:
        bias = Tensor()
    return _C.conv1d(input, weight, bias, stride, padding, dilation, groups)



[docs]
def conv2d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1):
    r"""Applies a 2D convolution over an input image composed of several input planes.

    See :class:`~tensorplay.nn.Conv2d` for details and output shape.

    Args:
        input: input tensor of shape :math:`(\text{minibatch} , \text{in\_channels} , iH , iW)`
        weight: filters of shape :math:`(\text{out\_channels} , \frac{\text{in\_channels}}{\text{groups}} , kH , kW)`
        bias: optional bias tensor of shape :math:`(\text{out\_channels})`. Default: ``None``
        stride: the stride of the convolving kernel. Can be a single number or a
          tuple `(sH, sW)`. Default: 1
        padding: implicit paddings on both sides of the input. Can be a single number or a tuple `(padH, padW)`. Default: 0
        dilation: the spacing between kernel elements. Can be a single number or
          a tuple `(dH, dW)`. Default: 1
        groups: split input into groups, both :math:`\text{in\_channels}` and :math:`\text{out\_channels}`
          should be divisible by the number of groups. Default: 1

    Examples::

        >>> # With square kernels and equal stride
        >>> filters = tp.randn(8, 4, 3, 3)
        >>> inputs = tp.randn(1, 4, 5, 5)
        >>> F.conv2d(inputs, filters, padding=1)
    """
    stride = _pair(stride)
    padding = _pair(padding)
    dilation = _pair(dilation)
    captured = _capture_call(
        conv2d,
        (input, weight, bias, stride, padding, dilation, groups),
        {},
    )
    if captured is not None:
        return captured
    if bias is None:
        bias = Tensor()
    return _C.conv2d(input, weight, bias, stride, padding, dilation, groups)



[docs]
def conv3d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1):
    r"""Applies a 3D convolution over an input image composed of several input planes.

    See :class:`~tensorplay.nn.Conv3d` for details and output shape.

    Args:
        input: input tensor of shape :math:`(\text{minibatch} , \text{in\_channels} , iD, iH , iW)`
        weight: filters of shape :math:`(\text{out\_channels} , \frac{\text{in\_channels}}{\text{groups}} , kD, kH , kW)`
        bias: optional bias tensor of shape :math:`(\text{out\_channels})`. Default: ``None``
        stride: the stride of the convolving kernel. Can be a single number or a
          tuple `(sD, sH, sW)`. Default: 1
        padding: implicit paddings on both sides of the input. Can be a single number or a tuple `(padD, padH, padW)`. Default: 0
        dilation: the spacing between kernel elements. Can be a single number or
          a tuple `(dD, dH, dW)`. Default: 1
        groups: split input into groups, both :math:`\text{in\_channels}` and :math:`\text{out\_channels}`
          should be divisible by the number of groups. Default: 1

    Examples::

        >>> # With square kernels and equal stride
        >>> filters = tp.randn(8, 4, 3, 3, 3)
        >>> inputs = tp.randn(1, 4, 5, 5, 5)
        >>> F.conv3d(inputs, filters, padding=1)
    """
    captured = _capture_call(conv3d, (input, weight, bias, stride, padding, dilation, groups), {})
    if captured is not None:
        return captured
    stride = _triple(stride)
    padding = _triple(padding)
    dilation = _triple(dilation)
    if bias is None:
        bias = Tensor()
    return _C.conv3d(input, weight, bias, stride, padding, dilation, groups)



[docs]
def conv_transpose2d(input, weight, bias=None, stride=1, padding=0, output_padding=0, groups=1, dilation=1):
    captured = _capture_call(conv_transpose2d, (input, weight, bias, stride, padding, output_padding, groups, dilation), {})
    if captured is not None:
        return captured
    stride = _pair(stride)
    padding = _pair(padding)
    output_padding = _pair(output_padding)
    dilation = _pair(dilation)
    if bias is None:
        bias = Tensor()
    return _C.conv_transpose2d(input, weight, bias, stride, padding, output_padding, groups, dilation)



[docs]
def conv_transpose3d(input, weight, bias=None, stride=1, padding=0, output_padding=0, groups=1, dilation=1):
    stride = _triple(stride)
    padding = _triple(padding)
    output_padding = _triple(output_padding)
    dilation = _triple(dilation)
    if bias is None:
        bias = Tensor()
    return _C.conv_transpose3d(input, weight, bias, stride, padding, output_padding, groups, dilation)



[docs]
def conv_transpose1d(input, weight, bias=None, stride=1, padding=0, output_padding=0, groups=1, dilation=1):
    stride = _single(stride)
    padding = _single(padding)
    output_padding = _single(output_padding)
    dilation = _single(dilation)
    if bias is None:
        bias = Tensor()
    return _C.conv_transpose1d(input, weight, bias, stride, padding, output_padding, groups, dilation)



[docs]
def unfold(input, kernel_size, dilation=1, padding=0, stride=1):
    r"""
    """
    if input.dim() not in (3, 4):
        raise ValueError(
            f"unfold: expected 3D (unbatched) or 4D input, got {input.dim()}D")
    return _C.im2col(input, _pair(kernel_size), _pair(dilation), _pair(padding), _pair(stride))



[docs]
def fold(input, output_size, kernel_size, dilation=1, padding=0, stride=1):
    r"""Combine an array of sliding local blocks into a tensor containing
    """
    if input.dim() not in (2, 3):
        raise ValueError(
            f"fold: expected 2D (unbatched) or 3D input, got {input.dim()}D")
    return _C.col2im(input, _pair(output_size), _pair(kernel_size), _pair(dilation),
                     _pair(padding), _pair(stride))



[docs]
def conv_tbc(input, weight, bias=None, pad=0):
    r"""Applies a 1D convolution over an input of shape (T, B, C) along the
    ``(kernel_width, in_channels, out_channels)``; the math is a standard
    cross-channel conv1d after permuting to (B, C, T).
    """
    if input.dim() != 3:
        raise ValueError("conv_tbc: input must have 3 dimensions (T, B, C)")
    if weight.dim() != 3:
        raise ValueError(
            "Weight tensor must have 3 dims: kernel_width, in_channels, out_channels.")
    if weight.size(1) != input.size(2):
        raise ValueError(
            f"Input dim 2 (input channels) is not == dim 1 in the weight tensor")
    x = input.permute(1, 2, 0)              # (B, C_in, T)
    w = weight.permute(2, 1, 0)             # (C_out, C_in, k)
    if bias is None:
        bias = Tensor()
    out = _C.conv1d(x, w.contiguous(), bias, (1,), (pad,), (1,), 1)
    return out.permute(2, 0, 1)             # (T, B, C_out)




[docs]
def max_pool2d(input, kernel_size, stride=None, padding=0, dilation=1, ceil_mode=False, return_indices=False):
    if return_indices:
        return max_pool2d_with_indices(
            input, kernel_size, stride=stride, padding=padding,
            dilation=dilation, ceil_mode=ceil_mode)
    kernel_size = _pair(kernel_size)
    if stride is None:
        stride = kernel_size
    else:
        stride = _pair(stride)
    padding = _pair(padding)
    dilation = _pair(dilation)
    captured = _capture_call(
        max_pool2d,
        (input, kernel_size, stride, padding, dilation, ceil_mode, return_indices),
        {},
    )
    if captured is not None:
        return captured
    # native kernel assumes contiguous layout; normalize views (no-op when
    return _C.max_pool2d(input.contiguous(), kernel_size, stride, padding,
                         dilation, ceil_mode)



[docs]
def avg_pool2d(input, kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True, divisor_override=None):
    captured = _capture_call(avg_pool2d, (input, kernel_size, stride, padding, ceil_mode, count_include_pad, divisor_override), {})
    if captured is not None:
        return captured
    kernel_size = _pair(kernel_size)
    if stride is None:
        stride = kernel_size
    else:
        stride = _pair(stride)
    padding = _pair(padding)
    return _C.avg_pool2d(input, kernel_size, stride, padding, ceil_mode, count_include_pad, divisor_override)



[docs]
def adaptive_avg_pool2d(input, output_size):
    # output_size can be int or (int, int) or (None, int) etc.
    output_size = _pair(output_size)
    captured = _capture_call(adaptive_avg_pool2d, (input, output_size), {})
    if captured is not None:
        return captured
    return _C.adaptive_avg_pool2d(input, output_size)



[docs]
def adaptive_max_pool2d(input, output_size):
    captured = _capture_call(adaptive_max_pool2d, (input, output_size), {})
    if captured is not None:
        return captured
    output_size = list(_pair(output_size))
    # Route through the (values, indices) op so autograd saves indices and the
    return _C.adaptive_max_pool2d_with_indices(input, output_size)[0]


# Normalization functions


[docs]
def batch_norm(input, running_mean=None, running_var=None, weight=None, bias=None, training=False, momentum=0.1, eps=1e-5):
    captured = _capture_call(
        batch_norm,
        (input, running_mean, running_var, weight, bias, training, momentum, eps),
        {},
    )
    if captured is not None:
        return captured
    return _C.batch_norm(input, weight, bias, running_mean, running_var, training, momentum, eps)



[docs]
def layer_norm(input, normalized_shape, weight=None, bias=None, eps=1e-5):
    captured = _capture_call(layer_norm, (input, normalized_shape, weight, bias, eps), {})
    if captured is not None:
        return captured
    normalized_shape = _single(normalized_shape)
    return _C.layer_norm(input, normalized_shape, weight, bias, eps)



[docs]
def group_norm(input, num_groups, weight=None, bias=None, eps=1e-5):
    captured = _capture_call(group_norm, (input, num_groups, weight, bias, eps), {})
    if captured is not None:
        return captured
    return _C.group_norm(input, num_groups, weight, bias, eps)



[docs]
def instance_norm(input, running_mean=None, running_var=None, weight=None, bias=None, use_input_stats=True, momentum=0.1, eps=1e-5):
    captured = _capture_call(instance_norm, (input, running_mean, running_var, weight, bias, use_input_stats, momentum, eps), {})
    if captured is not None:
        return captured
    return _C.instance_norm(input, weight, bias, running_mean, running_var, use_input_stats, momentum, eps)



[docs]
def pad(input, pad, mode='constant', value=0):
    r"""Pads tensor.  ``pad`` values are described starting from the last
    dimension and may cover any suffix of the input dimensions.
    """
    captured = _capture_call(globals()["pad"], (input, pad, mode, value), {})
    if captured is not None:
        return captured
    if mode == 'constant':
        return _C.constant_pad_nd(input, list(pad), value)
    ndim = input.dim()
    pad = list(pad)
    if len(pad) % 2 != 0:
        raise ValueError(f"padding length must be even, got {len(pad)}")
    if len(pad) > 2 * ndim:
        raise ValueError(
            f"padding length {len(pad)} exceeds the {ndim}-D input"
        )
    if mode == 'reflect':
        return _C.reflection_pad_nd(input, pad)
    if mode == 'replicate':
        return _C.replication_pad_nd(input, pad)
    if mode == 'circular':
        return _C.circular_pad_nd(input, pad)
    raise ValueError(f"Padding mode '{mode}' not supported")


# Loss functions

[docs]
def mse_loss(input, target, reduction='mean'):
    captured = _capture_call(mse_loss, (input, target, reduction), {})
    if captured is not None:
        return captured
    if not (target.size() == input.size()):
        print(f"Warning: Using a target size ({target.size()}) that is different to the input size ({input.size()}). "
              "This will likely lead to incorrect results due to broadcasting. "
              "Please ensure they have the same size.")
    
    reduction_enum = 1
    if reduction == 'none': reduction_enum = 0
    elif reduction == 'mean': reduction_enum = 1
    elif reduction == 'sum': reduction_enum = 2
    else: raise ValueError(f"{reduction} is not a valid value for reduction")

    return _C.mse_loss(input, target, reduction_enum)


def _nll_loss_red_enum(reduction):
    if reduction == 'none': return 0
    elif reduction == 'mean': return 1
    elif reduction == 'sum': return 2
    raise ValueError(f"{reduction} is not a valid value for reduction")


[docs]
def nll_loss(input, target, weight=None, size_average=None, ignore_index=-100,
             reduce=None, reduction='mean'):
    r"""The negative log likelihood loss.

    ``input`` with a scalar ``target``, 2D ``(N, C)``, and N-d
    ``target``.

    See :class:`~tensorplay.nn.NLLLoss` for details.
    """
    captured = _capture_call(nll_loss, (input, target, weight, size_average, ignore_index, reduce, reduction), {})
    if captured is not None:
        return captured
    if size_average is not None or reduce is not None:
        if size_average is None: size_average = True
        if reduce is None: reduce = True
        if not reduce: reduction = 'none'
        elif size_average: reduction = 'mean'
        else: reduction = 'sum'

    reduction_enum = _nll_loss_red_enum(reduction)

    if input.dim() <= 2:
        # nll_loss returns (output, total_weight)
        output, _ = _C.nll_loss(input, target, weight, reduction_enum, ignore_index)
        return output

    if input.dim() == 4:
        # input with (N, H, W) target; autograd flows through
        # nll_loss2d_backward.
        t = target if target.dtype == DType.int64 else target.to(DType.int64)
        output, _ = _C.nll_loss2d(input, t, weight, reduction_enum, ignore_index)
        return output

    # every spatial position acts as its own batch row, so move classes last,
    # flatten to (-1, C), run the 2-D kernel and restore the target shape.
    if tuple(target.size())[1:] != tuple(input.size())[2:]:
        expected = tuple(input.size()[:1] + input.size()[2:])
        raise ValueError(f"Expected target size {expected}, got {tuple(target.size())}")
    n = input.size(0)
    c = input.size(1)
    # (N, C, d_1, ..., d_k) -> (N, d_1, ..., d_k, C) -> (N * prod(d_i), C);
    # row order matches target's contiguous flattening.
    x = input.permute([0] + list(range(2, input.dim())) + [1]).contiguous().reshape(-1, c)
    t = target.contiguous().reshape(-1)
    if t.dtype != DType.int64:
        t = t.to(DType.int64)
    output, _ = _C.nll_loss(x, t, weight, reduction_enum, ignore_index)
    if reduction == 'none':
        output = output.reshape(target.size())
    return output



[docs]
def cross_entropy(input, target, weight=None, size_average=None, ignore_index=-100,
                  reduce=None, reduction='mean', label_smoothing=0.0):
    r"""Compute the cross entropy loss between input logits and target.

    the class-probability path, positive ``label_smoothing`` blends the NLL
    with a smoothed uniform term, and otherwise this is
    ``nll_loss(log_softmax(input), target)`` with N-d support.

    See :class:`~tensorplay.nn.CrossEntropyLoss` for details.
    """
    captured = _capture_call(cross_entropy, (input, target, weight, size_average, ignore_index, reduce, reduction, label_smoothing), {})
    if captured is not None:
        return captured
    if size_average is not None or reduce is not None:
        reduction = _legacy_get_string(size_average, reduce)

    # Hot path for the common training-loop case ((N, C) logits, int64
    # class targets, no class weights / label smoothing): go straight to
    # the native ops, skipping the legacy-reduction and N-d shape handling.
    if (weight is None and label_smoothing == 0.0 and input.dim() == 2
            and target.dtype == tensorplay.int64):
        red = 1 if reduction == 'mean' else (
            2 if reduction == 'sum' else (0 if reduction == 'none' else -1))
        if red >= 0:
            output, _ = _C.nll_loss(
                _C.log_softmax(input, 1, tensorplay.undefined),
                target, None, red, ignore_index)
            return output

    class_dim = 0 if input.dim() == 1 else 1
    n_classes = input.size(class_dim)
    if weight is not None and (weight.dim() != 1 or weight.numel() != n_classes):
        raise ValueError(
            f"cross_entropy: weight tensor should be defined either for all "
            f"{n_classes} classes or no classes but got weight tensor of "
            f"shape: {tuple(weight.size())}")

    if tuple(target.size()) == tuple(input.size()):
        # Soft targets when input and target shapes are the same
        # Handle matching input and target shapes as class probabilities.
        if ignore_index >= 0:
            raise ValueError("ignore_index is not supported for floating point target")
        if label_smoothing > 1.0:
            raise ValueError(f"label_smoothing must be between 0.0 and 1.0. Got: {label_smoothing}")
        input_ = log_softmax(input, class_dim)
        if label_smoothing > 0.0:
            target = target * (1 - label_smoothing) + label_smoothing / n_classes
        if weight is not None:
            w_shape = [1] * input.dim()
            w_shape[class_dim] = n_classes
            loss = -(input_ * target * weight.view(w_shape)).sum(class_dim)
        else:
            loss = -(input_ * target).sum(class_dim)
        if reduction == "none":
            return loss
        total = loss.sum()
        if reduction == "sum":
            return total
        if input.numel() == 0:
            return tensorplay.full([], float("nan"), dtype=total.dtype, device=total.device)
        return total / (input.numel() / n_classes)

    if label_smoothing > 0.0:
        # Blend the class loss with a uniform class distribution.
        if label_smoothing > 1.0:
            raise ValueError(f"label_smoothing must be between 0.0 and 1.0. Got: {label_smoothing}")
        input_ = log_softmax(input, class_dim)
        nllloss = nll_loss(input_, target, weight, None, ignore_index, None, reduction)
        if weight is not None:
            w_shape = [1] * input_.dim()
            w_shape[class_dim] = n_classes
            smooth_loss = -(input_ * weight.view(w_shape)).sum(class_dim)
        else:
            smooth_loss = -input_.sum(class_dim)
        ignore_mask = target.eq(ignore_index)
        smooth_loss = smooth_loss.masked_fill(ignore_mask, 0.0)
        if reduction == "mean":
            if weight is not None:
                filtered_target = target.masked_fill(ignore_mask, 0)
                tgt_weights = weight.index_select(0, filtered_target.reshape(-1))
                weight_sum = tgt_weights.masked_fill_(ignore_mask.reshape(-1), 0).sum()
                ret = smooth_loss.sum() / weight_sum
            else:
                true_mask = tensorplay.logical_not(ignore_mask)
                ret = smooth_loss.sum() / true_mask.to(smooth_loss.dtype).sum()
        elif reduction == "sum":
            ret = smooth_loss.sum()
        elif reduction == "none":
            ret = smooth_loss
        else:
            raise ValueError(f"{reduction} is not valid")
        return (1 - label_smoothing) * nllloss + ret * (label_smoothing / n_classes)

    return nll_loss(log_softmax(input, class_dim), target, weight, None, ignore_index, None, reduction)



# -----------------------------------------------------------------------------
# Activation / misc functions.  These are thin wrappers over the native
# dispatcher ops declared in the native schema; the element-wise
# -----------------------------------------------------------------------------


[docs]
def gelu(input: Tensor, approximate: str = 'none') -> Tensor:
    r"""gelu(input, approximate='none') -> Tensor

    When `approximate` is 'none', applies
    :math:`\text{GELU}(x) = x * \Phi(x)`; 'tanh' uses the tanh estimation.
    """
    captured = _capture_call(gelu, (input,), {"approximate": approximate})
    if captured is not None:
        return captured
    return tensorplay._C.gelu(self=input, approximate=approximate)




[docs]
def relu6(input: Tensor, inplace: bool = False) -> Tensor:
    r"""relu6(input, inplace=False) -> Tensor

    """
    out = tensorplay.relu6(input)
    return input.copy_(out) if inplace else out




[docs]
def hardswish(input: Tensor, inplace: bool = False) -> Tensor:
    r"""hardswish(input, inplace=False) -> Tensor"""
    out = tensorplay.hardswish(input)
    return input.copy_(out) if inplace else out




[docs]
def hardsigmoid(input: Tensor, inplace: bool = False) -> Tensor:
    r"""hardsigmoid(input, inplace=False) -> Tensor"""
    out = tensorplay.hardsigmoid(input)
    return input.copy_(out) if inplace else out




[docs]
def leaky_relu(input: Tensor, negative_slope: float = 0.01, inplace: bool = False) -> Tensor:
    r"""leaky_relu(input, negative_slope=0.01, inplace=False) -> Tensor"""
    out = tensorplay.leaky_relu(input, negative_slope)
    return input.copy_(out) if inplace else out




[docs]
def softplus(input: Tensor, beta: float = 1.0, threshold: float = 20.0) -> Tensor:
    r"""softplus(input, beta=1, threshold=20) -> Tensor"""
    return tensorplay.softplus(input, beta, threshold)




[docs]
def elu(input: Tensor, alpha: float = 1.0, inplace: bool = False) -> Tensor:
    r"""elu(input, alpha=1, inplace=False) -> Tensor"""
    out = tensorplay.elu(input, alpha)
    return input.copy_(out) if inplace else out




[docs]
def mish(input: Tensor, inplace: bool = False) -> Tensor:
    r"""mish(input, inplace=False) -> Tensor"""
    out = tensorplay.mish(input)
    return input.copy_(out) if inplace else out




[docs]
def selu(input: Tensor, inplace: bool = False) -> Tensor:
    r"""selu(input, inplace=False) -> Tensor"""
    out = tensorplay.selu(input)
    return input.copy_(out) if inplace else out




[docs]
def celu(input: Tensor, alpha: float = 1.0, inplace: bool = False) -> Tensor:
    r"""celu(input, alpha=1, inplace=False) -> Tensor"""
    out = tensorplay.celu(input, alpha)
    return input.copy_(out) if inplace else out



threshold_ = _add_docstr(
    _C.threshold_,
    r"""
threshold_(input, threshold, value) -> Tensor

In-place version of :func:`~threshold`.
""",
)

relu_ = _add_docstr(
    _C.relu_,
    r"""
relu_(input) -> Tensor

In-place version of :func:`~relu`.
""",
)

hardtanh_ = _add_docstr(
    _C.hardtanh_,
    r"""
hardtanh_(input, min_val=-1., max_val=1.) -> Tensor

In-place version of :func:`~hardtanh`.
""",
)

elu_ = _add_docstr(
    _C.elu_,
    r"""
elu_(input, alpha=1.) -> Tensor

In-place version of :func:`~elu`.
""",
)

selu_ = _add_docstr(
    _C.selu_,
    r"""
selu_(input) -> Tensor

In-place version of :func:`~selu`.
""",
)

celu_ = _add_docstr(
    _C.celu_,
    r"""
celu_(input, alpha=1.) -> Tensor

In-place version of :func:`~celu`.
""",
)

leaky_relu_ = _add_docstr(
    _C.leaky_relu_,
    r"""
leaky_relu_(input, negative_slope=0.01) -> Tensor

In-place version of :func:`~leaky_relu`.
""",
)



[docs]
def lstm_cell(input, hx, cx, w_ih, w_hh, b_ih=None, b_hh=None):
    r"""lstm_cell(input, hx, cx, w_ih, w_hh, b_ih=None, b_hh=None) -> (Tensor, Tensor)

    One time step of a long short-term memory cell. Returns the next
    hidden state and next cell state.
    """
    return _C.lstm_cell(input, hx, cx, w_ih, w_hh, b_ih, b_hh)




[docs]
def rnn_relu_cell(input, hx, w_ih, w_hh, b_ih=None, b_hh=None):
    r"""rnn_relu_cell(input, hx, w_ih, w_hh, b_ih=None, b_hh=None) -> Tensor

    One time step of an Elman RNN cell with ReLU nonlinearity.
    """
    return _C.rnn_relu_cell(input, hx, w_ih, w_hh, b_ih, b_hh)




[docs]
def rnn_tanh_cell(input, hx, w_ih, w_hh, b_ih=None, b_hh=None):
    r"""rnn_tanh_cell(input, hx, w_ih, w_hh, b_ih=None, b_hh=None) -> Tensor

    One time step of an Elman RNN cell with tanh nonlinearity.
    """
    return _C.rnn_tanh_cell(input, hx, w_ih, w_hh, b_ih, b_hh)




[docs]
def gru_cell(input, hx, w_ih, w_hh, b_ih=None, b_hh=None):
    r"""gru_cell(input, hx, w_ih, w_hh, b_ih=None, b_hh=None) -> Tensor

    One time step of a gated recurrent unit cell.
    """
    return _C.gru_cell(input, hx, w_ih, w_hh, b_ih, b_hh)




[docs]
def glu(input: Tensor, dim: int = -1) -> Tensor:
    r"""glu(input, dim=-1) -> Tensor

    Gated Linear Unit: :math:`a * \sigma(b)` where the input is split in half
    along ``dim``.
    """
    return tensorplay.glu(input, dim)




[docs]
def normalize(input: Tensor, p: float = 2.0, dim: int = 1, eps: float = 1e-12) -> Tensor:
    r"""normalize(input, p=2, dim=1, eps=1e-12) -> Tensor

    Performs :math:`L_p` normalization over the specified dimension —
    """
    denom = input.norm([dim], p, True).clamp_min(eps)
    return input / denom



def one_hot(tensor: Tensor, num_classes: int = -1) -> Tensor:
    r"""one_hot(tensor, num_classes=-1) -> Tensor"""
    return tensorplay.one_hot(tensor, num_classes)



[docs]
def interpolate(
    input: Tensor,
    size=None,
    scale_factor=None,
    mode: str = 'nearest',
    align_corners=None,
    recompute_scale_factor=None,
    antialias: bool = False,
) -> Tensor:
    r"""interpolate(input, size=None, scale_factor=None, mode='nearest',
    align_corners=None) -> Tensor

    Routes to the native ``upsample_*`` ops exactly like
    """
    captured = _capture_call(interpolate, (input, size, scale_factor, mode, align_corners, recompute_scale_factor, antialias), {})
    if captured is not None:
        return captured
    if size is None and scale_factor is None:
        raise ValueError("need to define size or scale_factor")
    if size is not None and scale_factor is not None:
        raise ValueError("only one of size or scale_factor should be defined")

    ndim = input.dim()
    if ndim < 3:
        raise ValueError(f"interpolate expects at least 3D input, got {ndim}D")
    spatial = ndim - 2
    align_corners_provided = align_corners is not None
    # mode branches can operate on a concrete size.
    if size is None and scale_factor is not None:
        if isinstance(scale_factor, (int, float)):
            scale_list = [scale_factor] * spatial
        else:
            scale_list = list(scale_factor)
            if len(scale_list) != spatial:
                raise ValueError(
                    f"scale_factor must have {spatial} values, got {len(scale_list)}"
                )
        import math
        if any(float(s) <= 0 for s in scale_list):
            raise ValueError("scale_factor values must be positive")
        size = [int(math.floor(float(input.size(2 + i)) * s))
                for i, s in enumerate(scale_list)]
    elif isinstance(size, int):
        size = [size] * spatial
    else:
        size = list(size)
        if len(size) != spatial:
            raise ValueError(
                f"size must have {spatial} values, got {len(size)}"
            )
    if any(int(s) <= 0 for s in size):
        raise ValueError(f"interpolate output sizes must be positive, got {size}")
    if mode in ('nearest', 'nearest-exact'):
        if align_corners is not None:
            raise ValueError("align_corners option can only be set with interpolating modes")
        # 'nearest-exact' resolves to the pixel-center kernels; the legacy
        # 'nearest' keeps the deprecated asymmetric-index behavior.
        if mode == 'nearest-exact':
            import tensorplay.functional as _functional
            if ndim == 3:
                return _functional._upsample_nearest_exact1d(input, size, None)
            elif ndim == 4:
                return _functional._upsample_nearest_exact2d(input, size, None)
            elif ndim == 5:
                return _functional._upsample_nearest_exact3d(input, size, None)
            raise ValueError(f"Expected 3D, 4D or 5D input, got {ndim}D")
        if ndim == 3:
            return tensorplay.upsample_nearest1d(input, size)
        elif ndim == 4:
            return tensorplay.upsample_nearest2d(input, size)
        elif ndim == 5:
            return tensorplay.upsample_nearest3d(input, size)
        raise ValueError(f"Expected 3D, 4D or 5D input, got {ndim}D")

    if align_corners is None:
        align_corners = False

    import math
    if scale_factor is not None:
        if isinstance(scale_factor, (int, float)):
            scale_factor = [scale_factor] * spatial
        elif len(scale_factor) != spatial:
            raise ValueError(
                f"scale_factor must have {spatial} values, got {len(scale_factor)}"
            )
        if any(float(f) <= 0 for f in scale_factor):
            raise ValueError("scale_factor values must be positive")
        if recompute_scale_factor:
            size = [int(math.floor(float(input.shape[2 + i]) * f)) for i, f in enumerate(scale_factor)]

    if mode == 'area':
        if align_corners_provided:
            raise ValueError("align_corners option cannot be set for area interpolation")
        if ndim == 3:
            return adaptive_avg_pool1d(input, size[0])
        if ndim == 4:
            return adaptive_avg_pool2d(input, size)
        if ndim == 5:
            return adaptive_avg_pool3d(input, size)
        raise ValueError(f"Expected 3D, 4D or 5D input, got {ndim}D")

    if mode == 'linear':
        if ndim != 3:
            raise ValueError("linear interpolation expects 3D input")
        if antialias:
            raise NotImplementedError("interpolate: antialias is not supported with mode='linear'")
        return tensorplay.upsample_linear1d(input, size, align_corners)
    elif mode == 'bilinear':
        if ndim != 4:
            raise ValueError("bilinear interpolation expects 4D input")
        if antialias:
            return _C._upsample_bilinear2d_aa(input, size, align_corners, None)
        return tensorplay.upsample_bilinear2d(input, size, align_corners)
    elif mode == 'bicubic':
        if ndim != 4:
            raise ValueError("bicubic interpolation expects 4D input")
        if antialias:
            return _C._upsample_bicubic2d_aa(input, size, align_corners, None)
        return tensorplay.upsample_bicubic2d(input, size, align_corners)
    elif mode == 'trilinear':
        if ndim != 5:
            raise ValueError("trilinear interpolation expects 5D input")
        if antialias:
            raise NotImplementedError("interpolate: antialias is not supported with mode='trilinear'")
        return tensorplay.upsample_trilinear3d(input, size, align_corners)
    raise ValueError(f"interpolate: mode '{mode}' is not supported")




[docs]
def multi_head_attention_forward(
    query: Tensor,
    key: Tensor,
    value: Tensor,
    embed_dim_to_check: int,
    num_heads: int,
    in_proj_weight: Tensor = None,
    in_proj_bias: Tensor = None,
    bias_k=None,
    bias_v=None,
    add_zero_attn: bool = False,
    dropout_p: float = 0.0,
    out_proj_weight: Tensor = None,
    out_proj_bias: Tensor = None,
    training: bool = True,
    key_padding_mask=None,
    need_weights: bool = True,
    attn_mask=None,
    use_separate_proj_weight: bool = False,
    q_proj_weight=None,
    k_proj_weight=None,
    v_proj_weight=None,
    static_k=None,
    static_v=None,
    average_attn_weights: bool = True,
    is_causal: bool = False,
):
    r"""multi_head_attention_forward(query, key, value, embed_dim_to_check,
    num_heads, in_proj_weight, in_proj_bias=None, bias_k=None, bias_v=None,
    add_zero_attn=False, dropout_p=0.0, out_proj_weight=None,
    out_proj_bias=None, training=True, key_padding_mask=None,
    need_weights=True, attn_mask=None, use_separate_proj_weight=False,
    q_proj_weight=None, k_proj_weight=None, v_proj_weight=None,
    static_k=None, static_v=None, average_attn_weights=True,
    is_causal=False) -> (Tensor, Optional[Tensor])

    Computes multi-head attention on (L, N, E) inputs (2D unbatched inputs are
    promoted internally and the batch dim is squeezed on return). Returns the
    projected output of shape (L, N, E) and, when ``need_weights`` is true,
    the attention weights of shape (N, L, S) — or (num_heads, L, S) with
    ``average_attn_weights=False``.
    """
    # Unbatched inputs carry a temporary batch dim; outputs squeeze it back.
    if query.dim() == 3:
        is_batched = True
        if key.dim() != 3 or value.dim() != 3:
            raise AssertionError(
                "for batched query, key and value must also be 3D")
        if key_padding_mask is not None and key_padding_mask.dim() > 2:
            raise AssertionError(
                "key_padding_mask must be 1D or 2D for batched input")
        if attn_mask is not None and attn_mask.dim() not in (2, 3):
            raise AssertionError(
                f"attn_mask must be 2D or 3D for batched input, got {attn_mask.dim()}D")
    elif query.dim() == 2:
        is_batched = False
        if key.dim() != 2 or value.dim() != 2:
            raise AssertionError(
                "for unbatched query, key and value must also be 2D")
        if key_padding_mask is not None and key_padding_mask.dim() != 1:
            raise AssertionError(
                "key_padding_mask must be 1D for unbatched input")
        if attn_mask is not None and attn_mask.dim() != 2:
            raise AssertionError(
                f"attn_mask must be 2D for unbatched input, got {attn_mask.dim()}D")
        query = query.unsqueeze(1)
        key = key.unsqueeze(1)
        value = value.unsqueeze(1)
        if key_padding_mask is not None:
            key_padding_mask = key_padding_mask.unsqueeze(0)
    else:
        raise AssertionError(
            f"query has to be 2d or 3d, but got {query.dim()}d")

    tgt_len, bsz, embed_dim = query.shape
    if num_heads <= 0:
        raise AssertionError(f"num_heads must be positive, got {num_heads}")
    if embed_dim_to_check != embed_dim:
        raise AssertionError(
            f"was expecting embedding dimension of {embed_dim_to_check}, "
            f"but got {embed_dim}")
    if key.shape[1] != bsz or value.shape[1] != bsz:
        raise AssertionError(
            "key and value batch dimensions must match the query batch "
            f"dimension ({bsz})")
    src_len = key.shape[0]
    head_dim = embed_dim // num_heads
    if head_dim * num_heads != embed_dim:
        raise AssertionError(
            f"embed_dim {embed_dim} not divisible by num_heads {num_heads}")

    if use_separate_proj_weight:
        if key.shape[:2] != value.shape[:2]:
            raise AssertionError(
                f"key's sequence and batch dims {tuple(key.shape[:2])} do not "
                f"match value's {tuple(value.shape[:2])}")
    elif key.shape != value.shape:
        raise AssertionError(
            f"key shape {tuple(key.shape)} does not match value shape {tuple(value.shape)}")

    # Bool masks become additive float masks so they can merge by addition.
    key_padding_mask = _canonical_mask(
        mask=key_padding_mask,
        mask_name="key_padding_mask",
        other_type=_none_or_dtype(attn_mask),
        other_name="attn_mask",
        target_type=query.dtype,
    )

    if is_causal and attn_mask is None and need_weights:
        causal_rows = tensorplay.arange(
            tgt_len, dtype=DType.int64, device=query.device).view(tgt_len, 1)
        causal_columns = tensorplay.arange(
            src_len, dtype=DType.int64, device=query.device).view(1, src_len)
        causal_fill = tensorplay.full(
            [tgt_len, src_len], float("-inf"), dtype=query.dtype,
            device=query.device)
        causal_mask = causal_rows < causal_columns
        attn_mask = tensorplay.where(
            causal_mask, causal_fill,
            tensorplay.zeros([tgt_len, src_len], dtype=query.dtype,
                             device=query.device))
        is_causal = False
    elif is_causal and attn_mask is None:
        raise RuntimeError(
            "Need attn_mask if specifying the is_causal hint. "
            "You may use the Transformer module method "
            "`generate_square_subsequent_mask` to create this mask.")

    if is_causal and key_padding_mask is None and not need_weights:
        # No mask fusion needed: pass the is_causal hint straight to SDPA.
        attn_mask = None
    else:
        attn_mask = _canonical_mask(
            mask=attn_mask,
            mask_name="attn_mask",
            other_type=None,
            other_name="",
            target_type=query.dtype,
            check_other=False,
        )
        if key_padding_mask is not None:
            # The merged mask is no longer causal.
            is_causal = False

    # compute in-projection
    if not use_separate_proj_weight:
        if in_proj_weight is None:
            raise AssertionError(
                "use_separate_proj_weight is False but in_proj_weight is None")
        q, k, v = _in_projection_packed(query, key, value, in_proj_weight, in_proj_bias)
    else:
        if q_proj_weight is None:
            raise AssertionError(
                "use_separate_proj_weight is True but q_proj_weight is None")
        if k_proj_weight is None:
            raise AssertionError(
                "use_separate_proj_weight is True but k_proj_weight is None")
        if v_proj_weight is None:
            raise AssertionError(
                "use_separate_proj_weight is True but v_proj_weight is None")
        if in_proj_bias is None:
            b_q = b_k = b_v = None
        else:
            b_q, b_k, b_v = tensorplay.chunk(in_proj_bias, 3)
        q, k, v = _in_projection(
            query, key, value, q_proj_weight, k_proj_weight, v_proj_weight,
            b_q, b_k, b_v,
        )

    # prep attention mask: promote a 2D mask to 3D (broadcast over batch).
    if attn_mask is not None:
        if attn_mask.dim() == 2:
            correct_2d_size = (tgt_len, src_len)
            if tuple(attn_mask.shape) != correct_2d_size:
                raise RuntimeError(
                    f"The shape of the 2D attn_mask is {tuple(attn_mask.shape)}, "
                    f"but should be {correct_2d_size}.")
            attn_mask = attn_mask.unsqueeze(0)
        elif attn_mask.dim() == 3:
            correct_3d_size = (bsz * num_heads, tgt_len, src_len)
            if tuple(attn_mask.shape) != correct_3d_size:
                raise RuntimeError(
                    f"The shape of the 3D attn_mask is {tuple(attn_mask.shape)}, "
                    f"but should be {correct_3d_size}.")
        else:
            raise RuntimeError(
                f"attn_mask's dimension {attn_mask.dim()} is not supported")

    # extra bias heads join the key/value sequences (dim 0, pre-reshape).
    if bias_k is not None and bias_v is not None:
        if static_k is not None:
            raise AssertionError("bias cannot be added to static key.")
        if static_v is not None:
            raise AssertionError("bias cannot be added to static value.")
        k = tensorplay.cat([k, bias_k.repeat(1, bsz, 1)])
        v = tensorplay.cat([v, bias_v.repeat(1, bsz, 1)])
        if attn_mask is not None:
            attn_mask = pad(attn_mask, (0, 1))
        if key_padding_mask is not None:
            key_padding_mask = pad(key_padding_mask, (0, 1))
    else:
        if bias_k is not None:
            raise AssertionError("bias_k is set but bias_v is None")
        if bias_v is not None:
            raise AssertionError("bias_v is set but bias_k is None")

    # reshape to heads-first (num_heads folded into the batch dim)
    q = q.reshape(tgt_len, bsz * num_heads, head_dim).transpose(0, 1)
    if static_k is None:
        k = k.reshape(k.shape[0], bsz * num_heads, head_dim).transpose(0, 1)
    else:
        if static_k.shape[0] != bsz * num_heads:
            raise AssertionError(
                f"expecting static_k.size(0) of {bsz * num_heads}, "
                f"but got {static_k.shape[0]}")
        if static_k.shape[2] != head_dim:
            raise AssertionError(
                f"expecting static_k.size(2) of {head_dim}, but got {static_k.shape[2]}")
        k = static_k
    if static_v is None:
        v = v.reshape(v.shape[0], bsz * num_heads, head_dim).transpose(0, 1)
    else:
        if static_v.shape[0] != bsz * num_heads:
            raise AssertionError(
                f"expecting static_v.size(0) of {bsz * num_heads}, "
                f"but got {static_v.shape[0]}")
        if static_v.shape[2] != head_dim:
            raise AssertionError(
                f"expecting static_v.size(2) of {head_dim}, but got {static_v.shape[2]}")
        v = static_v

    # extra zero key/value joined along the (heads-first) sequence dim.
    if add_zero_attn:
        zero_attn_shape = (bsz * num_heads, 1, head_dim)
        k = tensorplay.cat(
            [k, tensorplay.zeros(zero_attn_shape, dtype=k.dtype, device=k.device)],
            dim=1,
        )
        v = tensorplay.cat(
            [v, tensorplay.zeros(zero_attn_shape, dtype=v.dtype, device=v.device)],
            dim=1,
        )
        if attn_mask is not None:
            attn_mask = pad(attn_mask, (0, 1))
        if key_padding_mask is not None:
            key_padding_mask = pad(key_padding_mask, (0, 1))

    src_len = k.shape[1]

    # merge key padding into the attention mask (broadcast over query heads)
    if key_padding_mask is not None:
        if key_padding_mask.shape[0] != bsz:
            raise AssertionError(
                f"Expected key_padded_mask.shape[0] to be {bsz}, "
                f"but got {key_padding_mask.shape[0]}")
        if key_padding_mask.shape[1] != src_len:
            raise AssertionError(
                f"Expected key_padded_mask.shape[1] to be {src_len}, "
                f"but got {key_padding_mask.shape[1]}")
        key_padding_mask = (
            key_padding_mask.reshape(bsz, 1, 1, src_len)
            .expand(-1, num_heads, -1, -1)
            .reshape(bsz * num_heads, 1, src_len)
        )
        if attn_mask is None:
            attn_mask = key_padding_mask
        else:
            attn_mask = attn_mask + key_padding_mask

    if not training:
        dropout_p = 0.0

    if need_weights:
        E = q.shape[2]
        q_scaled = q * math.sqrt(1.0 / float(E))

        if attn_mask is not None:
            attn_output_weights = tensorplay.baddbmm(
                attn_mask.to(q_scaled.dtype), q_scaled, k.transpose(-2, -1)
            )
        else:
            attn_output_weights = tensorplay.bmm(q_scaled, k.transpose(-2, -1))
        attn_output_weights = softmax(attn_output_weights, dim=-1)
        if dropout_p > 0.0:
            attn_output_weights = dropout(attn_output_weights, p=dropout_p)

        attn_output = tensorplay.bmm(attn_output_weights, v)
        attn_output = attn_output.transpose(0, 1).reshape(tgt_len * bsz, embed_dim)
        attn_output = linear(attn_output, out_proj_weight, out_proj_bias)
        attn_output = attn_output.reshape(tgt_len, bsz, attn_output.shape[1])

        attn_output_weights = attn_output_weights.reshape(bsz, num_heads, tgt_len, src_len)
        if average_attn_weights:
            attn_output_weights = attn_output_weights.mean(dim=1)

        if not is_batched:
            attn_output = attn_output.squeeze(1)
            attn_output_weights = attn_output_weights.squeeze(0)
        return attn_output, attn_output_weights
    else:
        # (1, L, S) masks broadcast per batch; (N*num_heads, L, S) fold to 4D.
        if attn_mask is not None:
            if attn_mask.shape[0] == 1 and attn_mask.dim() == 3:
                attn_mask = attn_mask.unsqueeze(0)
            else:
                attn_mask = attn_mask.reshape(bsz, num_heads, -1, src_len)

        q = q.reshape(bsz, num_heads, tgt_len, head_dim)
        k = k.reshape(bsz, num_heads, src_len, head_dim)
        v = v.reshape(bsz, num_heads, src_len, head_dim)

        attn_output = scaled_dot_product_attention(
            q, k, v, attn_mask, dropout_p, is_causal
        )
        attn_output = attn_output.permute(2, 0, 1, 3).reshape(bsz * tgt_len, embed_dim)

        attn_output = linear(attn_output, out_proj_weight, out_proj_bias)
        attn_output = attn_output.reshape(tgt_len, bsz, attn_output.shape[1])
        if not is_batched:
            attn_output = attn_output.squeeze(1)
        return attn_output, None



# -----------------------------------------------------------------------------
# dispatcher ops available here, following the formulas in
# -----------------------------------------------------------------------------


def _get_reduction_enum(reduction: str) -> int:
    if reduction == 'none':
        return 0
    elif reduction == 'mean':
        return 1
    elif reduction == 'sum':
        return 2
    raise ValueError(f"{reduction} is not valid")


def _legacy_get_string(size_average, reduce):
    if size_average is None:
        size_average = True
    if reduce is None:
        reduce = True
    if size_average and reduce:
        return 'mean'
    elif reduce:
        return 'sum'
    else:
        return 'none'



[docs]
def logsigmoid(input: Tensor) -> Tensor:
    r"""logsigmoid(input) -> Tensor

    Applies element-wise :math:`\text{LogSigmoid}(x_i) = \log \left(\frac{1}{1 + \exp(-x_i)}\right)`

    See :class:`~tensorplay.nn.LogSigmoid` for more details.
    """
    # autograd flows through log_sigmoid_backward.
    return _C.log_sigmoid(input)




[docs]
def softmin(input: Tensor, dim: Optional[int] = None, dtype=None) -> Tensor:
    r"""Apply a softmin function.

    Note that :math:`\text{Softmin}(x) = \text{Softmax}(-x)`.

    See :class:`~tensorplay.nn.Softmin` for more details.
    """
    if dim is None:
        dim = input.dim() - 1 if input.dim() in (0, 1, 3) else 1
    return softmax(-input, dim=dim, dtype=dtype)




[docs]
def softsign(input: Tensor) -> Tensor:
    r"""softsign(input) -> Tensor

    Applies element-wise, the function :math:`\text{SoftSign}(x) = \frac{x}{1 + |x|}`

    See :class:`~tensorplay.nn.Softsign` for more details.
    """
    return input / (input.abs() + 1)




[docs]
def tanhshrink(input: Tensor) -> Tensor:
    r"""tanhshrink(input) -> Tensor

    Applies element-wise, :math:`\text{Tanhshrink}(x) = x - \text{Tanh}(x)`

    See :class:`~tensorplay.nn.Tanhshrink` for more details.
    """
    return input - tensorplay.tanh(input)




[docs]
def hardtanh(
    input: Tensor, min_val: float = -1.0, max_val: float = 1.0,
    inplace: bool = False,
) -> Tensor:
    r"""hardtanh(input, min_val=-1.0, max_val=1.0, inplace=False) -> Tensor"""
    result = tensorplay.hardtanh(input, min_val=min_val, max_val=max_val)
    if inplace:
        return input.copy_(result)
    return result




[docs]
def hardshrink(input: Tensor, lambd: float = 0.5) -> Tensor:
    r"""hardshrink(input, lambd=0.5) -> Tensor

    Applies the hard shrinkage function element-wise.

    See :class:`~tensorplay.nn.Hardshrink` for more details.
    """
    return tensorplay.hardshrink(input, lambd)




[docs]
def softshrink(input: Tensor, lambd: float = 0.5) -> Tensor:
    r"""softshrink(input, lambd=0.5) -> Tensor

    Applies the soft shrinkage function element-wise.

    See :class:`~tensorplay.nn.Softshrink` for more details.
    """
    return tensorplay.softshrink(input, lambd)




[docs]
def rrelu(
    input: Tensor,
    lower: float = 1.0 / 8,
    upper: float = 1.0 / 3,
    training: bool = False,
    inplace: bool = False,
) -> Tensor:
    r"""rrelu(input, lower=1./8, upper=1./3, training=False, inplace=False) -> Tensor

    Randomized leaky ReLU.

    See :class:`~tensorplay.nn.RReLU` for more details.
    """
    if training:
        noise = lower + (upper - lower) * _C.rand(input.shape, device=input.device)
        result = _C.rrelu_with_noise(input, noise, lower, upper, True)
    else:
        # Eval ignores noise (leaky slope (lower+upper)/2); pass input itself
        # to avoid an allocation.
        result = _C.rrelu_with_noise(input, input, lower, upper, False)
    if inplace:
        return input.copy_(result)
    return result



# -----------------------------------------------------------------------------
# Pooling helpers
# -----------------------------------------------------------------------------



[docs]
def avg_pool1d(
    input: Tensor,
    kernel_size,
    stride=None,
    padding=0,
    ceil_mode: bool = False,
    count_include_pad: bool = True,
    divisor_override=None,
) -> Tensor:
    r"""avg_pool1d(input, kernel_size, stride=None, padding=0, ceil_mode=False,
    count_include_pad=True, divisor_override=None) -> Tensor

    Applies a 1D average pooling over an input signal composed of several
    input planes. Input shape ``(N, C, L)`` or unbatched ``(C, L)``.
    """
    captured = _capture_call(avg_pool1d, (input, kernel_size, stride, padding, ceil_mode, count_include_pad, divisor_override), {})
    if captured is not None:
        return captured
    unbatched = input.dim() == 2
    x = input.unsqueeze(0) if unbatched else input
    k = _single(kernel_size)[0]
    s = k if stride is None else _single(stride)[0]
    p = _single(padding)[0]
    out = avg_pool2d(
        x.unsqueeze(3), (k, 1), (s, 1), (p, 0), ceil_mode,
        count_include_pad, divisor_override,
    ).squeeze(3)
    return out.squeeze(0) if unbatched else out




[docs]
def max_pool1d(
    input: Tensor,
    kernel_size,
    stride=None,
    padding=0,
    dilation=1,
    ceil_mode: bool = False,
    return_indices: bool = False,
):
    r"""max_pool1d(input, kernel_size, stride=None, padding=0, dilation=1,
    ceil_mode=False, return_indices=False) -> Tensor

    Applies a 1D max pooling over an input signal composed of several input
    planes. Input shape ``(N, C, L)`` or unbatched ``(C, L)``.
    """
    captured = _capture_call(max_pool1d, (input, kernel_size, stride, padding, dilation, ceil_mode, return_indices), {})
    if captured is not None:
        return captured
    if return_indices:
        return max_pool1d_with_indices(
            input, kernel_size, stride=stride, padding=padding,
            dilation=dilation, ceil_mode=ceil_mode)
    unbatched = input.dim() == 2
    x = input.unsqueeze(0) if unbatched else input
    k = _single(kernel_size)[0]
    s = k if stride is None else _single(stride)[0]
    p = _single(padding)[0]
    d = _single(dilation)[0]
    out = max_pool2d(
        x.unsqueeze(3), (k, 1), (s, 1), (p, 0), (d, 1), ceil_mode,
    ).squeeze(3)
    return out.squeeze(0) if unbatched else out




[docs]
def adaptive_avg_pool1d(input: Tensor, output_size) -> Tensor:
    r"""adaptive_avg_pool1d(input, output_size) -> Tensor"""
    unbatched = input.dim() == 2
    x = input.unsqueeze(0) if unbatched else input
    out = adaptive_avg_pool2d(x.unsqueeze(3), (output_size, 1)).squeeze(3)
    return out.squeeze(0) if unbatched else out




[docs]
def adaptive_max_pool1d(input: Tensor, output_size):
    r"""adaptive_max_pool1d(input, output_size) -> Tensor"""
    unbatched = input.dim() == 2
    x = input.unsqueeze(0) if unbatched else input
    out = adaptive_max_pool2d(x.unsqueeze(3), (output_size, 1)).squeeze(3)
    return out.squeeze(0) if unbatched else out




[docs]
def lp_pool2d(
    input: Tensor,
    norm_type,
    kernel_size,
    stride=None,
    ceil_mode: bool = False,
) -> Tensor:
    r"""Apply a 2D power-average pooling over an input signal.

    If the sum of all inputs to the power of `p` is zero, the gradient is set
    to zero as well.

    See :class:`~tensorplay.nn.LPPool2d` for details.
    """
    kw, kh = _pair(kernel_size)
    if isinstance(norm_type, (int, float)):
        if norm_type == 0:
            raise ValueError(f"norm_type must be a non-zero value, but got {norm_type}")
        if norm_type == float("inf"):
            return max_pool2d(input.abs(), kernel_size, stride, 0, 1, ceil_mode)
        if norm_type == -float("inf"):
            return -max_pool2d(-input.abs(), kernel_size, stride, 0, 1, ceil_mode)

    if stride is not None:
        out = avg_pool2d(input.pow(norm_type), kernel_size, stride, 0, ceil_mode)
    else:
        out = avg_pool2d(input.pow(norm_type), kernel_size, padding=0, ceil_mode=ceil_mode)

    return (tensorplay.sign(out) * relu(tensorplay.abs(out))).mul(kw * kh).pow(1.0 / norm_type)




[docs]
def lp_pool1d(
    input: Tensor,
    norm_type,
    kernel_size,
    stride=None,
    ceil_mode: bool = False,
) -> Tensor:
    r"""Apply a 1D power-average pooling over an input signal.

    See :class:`~tensorplay.nn.LPPool1d` for details.
    """
    k = _single(kernel_size)[0]
    s = None if stride is None else _single(stride)[0]

    if isinstance(norm_type, (int, float)):
        if norm_type == 0:
            raise ValueError(f"norm_type must be a non-zero value, but got {norm_type}")
        if norm_type == float("inf"):
            return max_pool1d(input.abs(), kernel_size, stride, 0, 1, ceil_mode)
        if norm_type == -float("inf"):
            return -max_pool1d(-input.abs(), kernel_size, stride, 0, 1, ceil_mode)

    if stride is not None:
        out = avg_pool1d(input.pow(norm_type), kernel_size, stride, 0, ceil_mode)
    else:
        out = avg_pool1d(input.pow(norm_type), kernel_size, padding=0, ceil_mode=ceil_mode)

    return (tensorplay.sign(out) * relu(tensorplay.abs(out))).mul(k).pow(1.0 / norm_type)




[docs]
def local_response_norm(
    input: Tensor,
    size: int,
    alpha: float = 1e-4,
    beta: float = 0.75,
    k: float = 1.0,
) -> Tensor:
    r"""Apply local response normalization over an input signal.

    The input signal is composed of several input planes, where channels
    occupy the second dimension. Normalization is applied across channels.

    See :class:`~tensorplay.nn.LocalResponseNorm` for details.
    """
    captured = _capture_call(local_response_norm, (input, size, alpha, beta, k), {})
    if captured is not None:
        return captured
    dim = input.dim()
    if dim < 3:
        raise ValueError(
            f"Expected 3D or higher dimensionality input (got {dim} dimensions)"
        )
    if input.numel() == 0:
        return input

    # Windowed sum of squares along the channel axis (dim 1), equivalent to
    div = input.mul(input)
    pad_left = size // 2
    pad_right = (size - 1) // 2

    def _zero_channels(n: int, ref: Tensor) -> Tensor:
        shape = list(ref.shape)
        shape[1] = n
        return tensorplay.zeros(shape, dtype=ref.dtype, device=ref.device)

    # The leading zero channel beyond ``pad_left`` makes the prefix sums
    # 1-indexed, so the window covering output channel ``i`` -- padded
    # channels ``[i, i + size)`` -- is exactly ``cs[i + size] - cs[i]``.
    parts = [_zero_channels(pad_left + 1, div), div]
    if pad_right:
        parts.append(_zero_channels(pad_right, div))
    padded = tensorplay.cat(parts, dim=1)

    cs = padded.cumsum(1)
    c = input.size(1)
    hi = tensorplay.narrow(cs, 1, size, c)
    lo = tensorplay.narrow(cs, 1, 0, c)
    window_sum = hi - lo

    div = window_sum.mul(alpha / size).add(k).pow(beta)
    return input / div




[docs]
def dropout1d(
    input: Tensor,
    p: float = 0.5,
    training: bool = True,
    inplace: bool = False,
) -> Tensor:
    r"""Randomly zero out entire channels (a channel is a 1D feature map).

    See :class:`~tensorplay.nn.Dropout1d` for details.
    """
    if p < 0.0 or p > 1.0:
        raise ValueError(f"dropout probability has to be between 0 and 1, but got {p}")
    inp_dim = input.dim()
    if inp_dim not in (2, 3):
        raise RuntimeError(
            f"dropout1d: Expected 2D or 3D input, but received a {inp_dim}D input. "
            "Note that dropout1d exists to provide channel-wise dropout on inputs with 1 "
            "spatial dimension, a channel dimension, and an optional batch dimension "
            "(i.e. 2D or 3D inputs)."
        )
    if not training or p == 0:
        return input

    if p == 1:
        result = _C.zeros_like(input)
        return input.copy_(result) if inplace else result

    is_batched = inp_dim == 3
    x = input if is_batched else input.unsqueeze(0)

    mask_shape = list(x.shape)
    mask_shape[2:] = [1] * (x.dim() - 2)
    mask = (_C.rand(mask_shape, device=x.device) > p).to(x.dtype)
    scale = 1.0 / (1.0 - p)

    if inplace:
        result = x.mul_(mask).mul_(scale)
    else:
        result = x * mask * scale

    if not is_batched:
        result = result.squeeze(0)
    return result




[docs]
def feature_alpha_dropout(
    input: Tensor,
    p: float = 0.5,
    training: bool = False,
    inplace: bool = False,
) -> Tensor:
    r"""Randomly masks out entire channels, setting activations to the
    negative saturation value of the SELU activation function.

    See :class:`~tensorplay.nn.FeatureAlphaDropout` for details.
    """
    if p < 0.0 or p > 1.0:
        raise ValueError(f"dropout probability has to be between 0 and 1, but got {p}")

    if input.dim() < 2:
        raise RuntimeError(
            f"feature_alpha_dropout: Expected input to have at least 2 dimensions, "
            f"but got {input.dim()}"
        )

    if not training or p == 0 or input.numel() == 0:
        return input

    if p == 1:
        result = _C.zeros_like(input)
        return input.copy_(result) if inplace else result

    alpha_c = 1.7580993408473766
    a = 1.0 / math.sqrt((alpha_c * alpha_c * p + 1) * (1 - p))
    b_coeff = alpha_c * a

    mask_shape = list(input.shape)
    mask_shape[2:] = [1] * (input.dim() - 2)
    noise = (_C.rand(mask_shape, device=input.device) > p).to(input.dtype)
    b = (noise - 1).mul(b_coeff).add(b_coeff * p)
    noise = noise.mul(a)

    result = input * noise + b
    if inplace:
        return input.copy_(result)
    return result



# -----------------------------------------------------------------------------
# Distance helpers
# -----------------------------------------------------------------------------



[docs]
def cosine_similarity(x1: Tensor, x2: Tensor, dim: int = 1, eps: float = 1e-8) -> Tensor:
    r"""Returns cosine similarity between x1 and x2, computed along dim."""
    denom = x1.norm([dim]).mul(x2.norm([dim]))
    denom = tensorplay.clamp(denom, min=eps)
    return (x1 * x2).sum(dim) / denom



# -----------------------------------------------------------------------------
# Mask helpers used by Transformer modules.
# -----------------------------------------------------------------------------


def _canonical_mask(
    mask: Optional[Tensor],
    mask_name: str,
    other_type=None,
    other_name: str = "",
    target_type=None,
    check_other: bool = True,
) -> Optional[Tensor]:
    if mask is not None:
        _mask_dtype = mask.dtype
        _mask_is_float = _mask_dtype in (
            DType.float16, DType.bfloat16, DType.float32, DType.float64,
        )
        if _mask_dtype != DType.bool and not _mask_is_float:
            raise AssertionError(
                f"only bool and floating types of {mask_name} are supported"
            )
        if check_other and other_type is not None:
            if _mask_dtype != other_type:
                warnings.warn(
                    f"Support for mismatched {mask_name} and {other_name} "
                    "is deprecated. Use same type for both instead.",
                    stacklevel=2,
                )
        if not _mask_is_float:
            mask = tensorplay.zeros_like(mask, dtype=target_type).masked_fill_(
                mask, float("-inf")
            )
    return mask


def _none_or_dtype(input: Optional[Tensor]):
    if input is None:
        return None
    elif isinstance(input, Tensor):
        return input.dtype
    raise RuntimeError("input to _none_or_dtype() must be None or Tensor")


# -----------------------------------------------------------------------------
# live in the native schema and backend kernels).
# -----------------------------------------------------------------------------



[docs]
def multilabel_soft_margin_loss(
    input: Tensor,
    target: Tensor,
    weight: Optional[Tensor] = None,
    size_average=None,
    reduce=None,
    reduction: str = "mean",
) -> Tensor:
    r"""Compute the multilabel soft margin loss.

    See :class:`~tensorplay.nn.MultiLabelSoftMarginLoss` for details.
    """
    if size_average is not None or reduce is not None:
        reduction = _legacy_get_string(size_average, reduce)

    loss = -(target * logsigmoid(input) + (1 - target) * logsigmoid(-input))

    if weight is not None:
        loss = loss * weight

    class_dim = input.dim() - 1
    C = input.size(class_dim)
    loss = loss.sum(class_dim) / C  # only return N loss values

    if reduction == "none":
        ret = loss
    elif reduction == "mean":
        ret = loss.mean()
    elif reduction == "sum":
        ret = loss.sum()
    else:
        ret = input
        raise ValueError(reduction + " is not valid")
    return ret




[docs]
def gaussian_nll_loss(
    input: Tensor,
    target: Tensor,
    var,
    full: bool = False,
    eps: float = 1e-6,
    reduction: str = "mean",
) -> Tensor:
    r"""Compute the Gaussian negative log likelihood loss.

    See :class:`~tensorplay.nn.GaussianNLLLoss` for details.
    """
    # Entries of var must be non-negative
    if isinstance(var, float):
        if var < 0:
            raise ValueError("var has negative entry/entries")
        var = tensorplay.ones_like(input) * var
    elif (var < 0).to(input.dtype).sum().item() > 0:
        raise ValueError("var has negative entry/entries")

    # Check var size
    if tuple(var.size()) != tuple(input.size()):
        # If var is one dimension short of input, but the sizes match otherwise,
        # then this is a homoscedastic case.
        if tuple(input.size())[:-1] == tuple(var.size()):
            var = tensorplay.unsqueeze(var, -1)
        elif (
            input.dim() == var.dim()
            and sum(y for x, y in zip(input.size(), var.size()) if x != y) == 1
        ):  # Heteroscedastic case
            pass
        else:
            raise ValueError("var is of incorrect size")

    # Check validity of reduction mode
    if reduction not in ("mean", "sum", "none"):
        raise ValueError(reduction + " is not valid")

    # Clamp for stability
    var = var.clone()
    with tensorplay.no_grad():
        var.clamp_(min=eps)

    # Calculate the loss
    loss = 0.5 * (tensorplay.log(var) + (input - target) ** 2 / var)
    if full:
        loss += 0.5 * math.log(2 * math.pi)

    if reduction == "mean":
        return loss.mean()
    elif reduction == "sum":
        return loss.sum()
    else:
        return loss




[docs]
def triplet_margin_with_distance_loss(
    anchor: Tensor,
    positive: Tensor,
    negative: Tensor,
    *,
    distance_function=None,
    margin: float = 1.0,
    swap: bool = False,
    reduction: str = "mean",
) -> Tensor:
    r"""Compute the triplet margin loss using a custom distance function.

    See :class:`~tensorplay.nn.TripletMarginWithDistanceLoss` for details.
    """
    # Check validity of reduction mode
    if reduction not in ("mean", "sum", "none"):
        raise ValueError(f"{reduction} is not a valid value for reduction")

    # Check validity of margin
    if margin <= 0:
        raise ValueError(f"margin must be greater than 0, got {margin}")

    # Check dimensions
    a_dim = anchor.dim()
    p_dim = positive.dim()
    n_dim = negative.dim()
    if not (a_dim == p_dim and p_dim == n_dim):
        raise RuntimeError(
            f"The anchor, positive, and negative tensors are expected to have "
            f"the same number of dimensions, but got: anchor {a_dim}D, "
            f"positive {p_dim}D, and negative {n_dim}D inputs"
        )

    # Calculate loss
    if distance_function is None:
        distance_function = tensorplay.pairwise_distance

    dist_pos = distance_function(anchor, positive)
    dist_neg = distance_function(anchor, negative)
    # The distance swap is described in the paper "Learning shallow
    # convolutional feature descriptors with triplet losses" by V. Balntas, E.
    # Riba et al.
    if swap:
        dist_swap = distance_function(positive, negative)
        dist_neg = tensorplay.minimum(dist_neg, dist_swap)
    loss = tensorplay.clamp(margin + dist_pos - dist_neg, 0.0)

    # Apply reduction
    if reduction == "sum":
        return loss.sum()
    elif reduction == "mean":
        return loss.mean()
    else:  # reduction == "none"
        return loss



# =============================================================================
# 2.15.0a0 @ 893b6406).  Ops that are native in this repo call the dispatcher
# directly; the rest are composed from dispatched primitives following the
# same math without new kernels.
# =============================================================================


def _broadcast_shapes(*shapes):
    out = []
    for dims in zip(*(reversed(s) for s in shapes)):
        d = 1
        for x in dims:
            if x != 1:
                if d != 1 and x != d:
                    raise ValueError("Shape mismatch: objects cannot be broadcast to a single shape")
                d = x
        out.append(d)
    return tuple(reversed(out))


def _apply_reduction(loss, reduction):
    if reduction == "none":
        return loss
    if reduction == "mean":
        return loss.mean()
    if reduction == "sum":
        return loss.sum()
    raise ValueError(f"{reduction} is not a valid value for reduction")


def _band(t, lo, hi):
    """t >= lo AND t < hi, elementwise (bool)."""
    return tensorplay.logical_and(t >= lo, t < hi)



[docs]
def one_hot(tensor: Tensor, num_classes: int = -1) -> Tensor:
    r"""one_hot(tensor, num_classes=-1) -> LongTensor

    Returns long tensor shaped ``tensor.shape + (num_classes,)`` with a 1 at
    """
    captured = _capture_call(one_hot, (tensor, num_classes), {})
    if captured is not None:
        return captured
    if num_classes < 0:
        if tensor.numel() == 0:
            raise RuntimeError("Cannot infer num classes from empty tensor")
        num_classes = int(tensor.max().item()) + 1
    t64 = tensor.to(DType.int64)
    rng = tensorplay.arange(num_classes, dtype=DType.int64, device=tensor.device)
    return t64.unsqueeze(-1).eq(rng).to(DType.int64)




[docs]
def sigmoid(input):
    r"""sigmoid(input) -> Tensor

    Applies the element-wise function :math:`\text{Sigmoid}(x) = \frac{1}{1 + \exp(-x)}`
    """
    return tensorplay.sigmoid(input)




[docs]
def tanh(input):
    r"""tanh(input) -> Tensor

    Applies element-wise :math:`\text{Tanh}(x) = \frac{\exp(x) - \exp(-x)}{\exp(x) + \exp(-x)}`
    """
    return tensorplay.tanh(input)




[docs]
def rms_norm(
    input: Tensor,
    normalized_shape,
    weight: Optional[Tensor] = None,
    eps: Optional[float] = None,
) -> Tensor:
    r"""Apply Root Mean Square Layer Normalization.

    Dispatches to the native fused kernel (single dispatch, CPU vectorized
    rows / CUDA block-per-row); falls back to the composite below under
    CompositeImplicitAutograd rms_norm."""
    return _rms_norm_impl(input, normalized_shape, weight, eps)



def _rms_norm_composite(
    input: Tensor,
    normalized_shape,
    weight: Optional[Tensor] = None,
    eps: Optional[float] = None,
) -> Tensor:
    r"""
    rms_norm composite (fp32 compute for reduced dtypes)."""
    shape = list(normalized_shape) if isinstance(normalized_shape, (list, tuple)) else [int(normalized_shape)]
    ndim = len(shape)
    dims = list(range(input.dim() - ndim, input.dim()))
    if eps is None:
        eps = 1e-5
    compute_dtype = DType.float32 if input.dtype in (DType.float16, DType.bfloat16) else input.dtype
    x = input.to(compute_dtype)
    denom = x.pow(2).mean(dims, keepdim=True) + eps
    inv = tensorplay.rsqrt(denom)
    out = x * inv
    if weight is not None:
        w_shape = [1] * (input.dim() - ndim) + list(shape)
        out = out * weight.to(compute_dtype).view(w_shape)
    return out.to(input.dtype)


def _rms_norm_impl(
    input: Tensor,
    normalized_shape,
    weight: Optional[Tensor] = None,
    eps: Optional[float] = None,
) -> Tensor:
    """Native fused kernel (CPU vectorized rows / CUDA block-per-row).

    Falls back to the composite above under autograd: the native forward has
    flows through its inner ops), so training graphs must keep composing."""
    needs_grad = tensorplay.is_grad_enabled() and (
        input.requires_grad
        or (weight is not None and getattr(weight, "requires_grad", False))
    )
    if needs_grad:
        return _rms_norm_composite(input, normalized_shape, weight, eps)
    return tensorplay._C.rms_norm(
        input,
        list(normalized_shape) if isinstance(normalized_shape, (list, tuple)) else [int(normalized_shape)],
        weight,
        float(eps) if eps is not None else None,
    )



[docs]
def gumbel_softmax(
    logits: Tensor,
    tau: float = 1,
    hard: bool = False,
    eps: float = 1e-10,
    dim: int = -1,
) -> Tensor:
    r"""Sample from the Gumbel-Softmax distribution and optionally discretize.

    straight-through when ``hard=True``).
    """
    if eps != 1e-10:
        warnings.warn("`eps` parameter is deprecated and has no effect.", stacklevel=2)
    u = tensorplay.empty_like(logits)
    u.exponential_()
    gumbels = -u.log()
    gumbels = (logits + gumbels) / tau
    y_soft = softmax(gumbels, dim=dim)
    if hard:
        idx = _C.argmax(y_soft, dim=dim, keepdim=True)
        ar = tensorplay.arange(y_soft.size(dim), dtype=DType.int64, device=logits.device)
        view = [1] * logits.dim()
        view[dim] = -1
        y_hard = idx.eq(ar.view(view)).to(logits.dtype)
        return y_hard.detach() - y_soft.detach() + y_soft
    return y_soft



# -----------------------------------------------------------------------------
# Loss functions (F-alignment)
# -----------------------------------------------------------------------------



[docs]
def l1_loss(
    input: Tensor,
    target: Tensor,
    size_average=None,
    reduce=None,
    reduction: str = "mean",
    weight: Optional[Tensor] = None,
) -> Tensor:
    r"""Compute the L1 loss, with optional weighting.

    Function that takes the mean element-wise absolute value difference.
    See :class:`~tensorplay.nn.L1Loss` for details.
    """
    captured = _capture_call(l1_loss, (input, target, size_average, reduce, reduction, weight), {})
    if captured is not None:
        return captured
    if size_average is not None or reduce is not None:
        reduction = _legacy_get_string(size_average, reduce)
    if target.size() != input.size():
        warnings.warn(
            f"Using a target size ({target.size()}) that is different to the input size ({input.size()}). "
            "This will likely lead to incorrect results due to broadcasting. "
            "Please ensure they have the same size.",
            stacklevel=2,
        )
    if weight is not None:
        if weight.size() != input.size():
            raise ValueError("Weights and input must have the same size.")
        absolute_errors = (input - target).abs()
        weighted = absolute_errors * weight
        if reduction == "none":
            return weighted
        if reduction == "sum":
            return weighted.sum()
        return weighted.sum() / weight.sum()

    expanded_input, expanded_target = _expand_pair(input, target)
    return _C.tp_l1_loss(expanded_input, expanded_target, _get_reduction_enum(reduction))



def _expand_pair(input: Tensor, target: Tensor):
    """Broadcast input/target to a common shape when they differ."""
    if tuple(input.size()) == tuple(target.size()):
        return input, target
    shape = _broadcast_shapes(tuple(input.size()), tuple(target.size()))
    return input.expand(shape), target.expand(shape)


_REDUCTION_STRINGS = {0: "none", 1: "mean", 2: "sum"}



[docs]
def smooth_l1_loss(
    input: Tensor,
    target: Tensor,
    size_average=None,
    reduce=None,
    reduction: str = "mean",
    beta: float = 1.0,
) -> Tensor:
    r"""Compute the Smooth L1 loss.

    Function uses a squared term if the absolute element-wise error falls
    below beta and an L1 term otherwise.
    See :class:`~tensorplay.nn.SmoothL1Loss` for details.
    """
    if size_average is not None or reduce is not None:
        reduction = _legacy_get_string(size_average, reduce)
    if target.size() != input.size():
        warnings.warn(
            f"Using a target size ({target.size()}) that is different to the input size ({input.size()}). "
            "This will likely lead to incorrect results due to broadcasting. "
            "Please ensure they have the same size.",
            stacklevel=2,
        )
    expanded_input, expanded_target = _expand_pair(input, target)
    enum_ = _get_reduction_enum(reduction)
    if beta == 0.0:
        return _C.tp_l1_loss(expanded_input, expanded_target, enum_)
    return _C.smooth_l1_loss(expanded_input, expanded_target, enum_, beta)




[docs]
def huber_loss(
    input: Tensor,
    target: Tensor,
    reduction: str = "mean",
    delta: float = 1.0,
    weight: Optional[Tensor] = None,
) -> Tensor:
    r"""Compute the Huber loss, with optional weighting.

    Function uses a squared term if the absolute error falls below delta and
    a delta-scaled L1 term otherwise.
    See :class:`~tensorplay.nn.HuberLoss` for details.
    """
    if target.size() != input.size():
        warnings.warn(
            f"Using a target size ({target.size()}) that is different to the input size ({input.size()}). "
            "This will likely lead to incorrect results due to broadcasting. "
            "Please ensure they have the same size.",
            stacklevel=2,
        )
    expanded_input, expanded_target = _expand_pair(input, target)
    enum_ = _get_reduction_enum(reduction)
    if weight is None:
        return _C.huber_loss(expanded_input, expanded_target, enum_, delta)

    if weight.size() != input.size():
        raise ValueError("Weights and input must have the same size.")
    unweighted = _C.huber_loss(expanded_input, expanded_target, 0, delta)
    weighted = unweighted * weight
    if reduction == "none":
        return weighted
    if reduction == "sum":
        return weighted.sum()
    return weighted.mean()




[docs]
def kl_div(
    input: Tensor,
    target: Tensor,
    size_average=None,
    reduce=None,
    reduction: str = "mean",
    log_target: bool = False,
) -> Tensor:
    r"""Compute the KL Divergence loss.

    ``input`` holds log-probabilities; see :class:`~tensorplay.nn.KLDivLoss`.
    Note that :attr:`reduction='mean'` divides by the number of elements and
    does not return the true KL divergence value — use ``'batchmean'``.
    """
    if size_average is not None or reduce is not None:
        reduction_enum = _get_reduction_enum(_legacy_get_string(size_average, reduce))
    else:
        if reduction == "mean":
            warnings.warn(
                "reduction: 'mean' divides the total loss by both the batch size and the support size."
                "'batchmean' divides only by the batch size, and aligns with the KL div math definition."
                "'mean' will be changed to behave the same as 'batchmean' in the next major release.",
                stacklevel=2,
            )
        if reduction == "batchmean":
            reduction_enum = 2  # sum
        else:
            reduction_enum = _get_reduction_enum(reduction)

    reduced = _C.tp_kl_div(input, target, reduction_enum, log_target)
    if reduction == "batchmean" and input.dim() != 0:
        reduced = reduced / input.size(0)
    return reduced




[docs]
def binary_cross_entropy(
    input: Tensor,
    target: Tensor,
    weight: Optional[Tensor] = None,
    size_average=None,
    reduce=None,
    reduction: str = "mean",
) -> Tensor:
    r"""Compute Binary Cross Entropy between the target and input probabilities.

    See :class:`~tensorplay.nn.BCELoss` for details.
    """
    if size_average is not None or reduce is not None:
        reduction_enum = _get_reduction_enum(_legacy_get_string(size_average, reduce))
    else:
        reduction_enum = _get_reduction_enum(reduction)
    if target.size() != input.size():
        raise ValueError(
            f"Using a target size ({target.size()}) that is different to the input size ({input.size()}) is deprecated. "
            "Please ensure they have the same size."
        )
    if weight is not None:
        new_size = _broadcast_shapes(tuple(target.size()), tuple(weight.size()))
        weight = weight.expand(new_size)
    return _C.binary_cross_entropy(input, target, weight, reduction_enum)




[docs]
def binary_cross_entropy_with_logits(
    input: Tensor,
    target: Tensor,
    weight: Optional[Tensor] = None,
    size_average=None,
    reduce=None,
    reduction: str = "mean",
    pos_weight: Optional[Tensor] = None,
) -> Tensor:
    r"""Compute Binary Cross Entropy between target and input logits.

    optionally rescaled by ``weight``, then reduced.
    See :class:`~tensorplay.nn.BCEWithLogitsLoss` for details.
    """
    if size_average is not None or reduce is not None:
        reduction_enum = _get_reduction_enum(_legacy_get_string(size_average, reduce))
    else:
        reduction_enum = _get_reduction_enum(reduction)
    if tuple(target.size()) != tuple(input.size()):
        raise ValueError(
            f"Target size ({target.size()}) must be the same as input size ({input.size()})"
        )

    log_sigmoid_input = logsigmoid(input)
    if pos_weight is not None:
        log_weight = (pos_weight - 1).mul(target).add(1)
        log_sigmoid_input = log_sigmoid_input.mul(log_weight)

    loss = (1 - target).mul(input).sub(log_sigmoid_input)
    if weight is not None:
        loss = loss.mul(weight)
    return _apply_reduction(loss, _REDUCTION_STRINGS[reduction_enum])




[docs]
def poisson_nll_loss(
    input: Tensor,
    target: Tensor,
    log_input: bool = True,
    full: bool = False,
    size_average=None,
    eps: float = 1e-8,
    reduce=None,
    reduction: str = "mean",
) -> Tensor:
    r"""Compute the Poisson negative log likelihood loss.

    See :class:`~tensorplay.nn.PoissonNLLLoss` for details.
    """
    if size_average is not None or reduce is not None:
        reduction = _legacy_get_string(size_average, reduce)
    if reduction not in ("none", "mean", "sum"):
        raise ValueError(f"{reduction} is not a valid value for reduction")
    return _C.tp_poisson_nll_loss(
        input, target, log_input, full, eps, _get_reduction_enum(reduction)
    )




[docs]
def soft_margin_loss(
    input: Tensor,
    target: Tensor,
    size_average=None,
    reduce=None,
    reduction: str = "mean",
) -> Tensor:
    r"""Compute the soft margin loss.

    See :class:`~tensorplay.nn.SoftMarginLoss` for details.
    """
    if size_average is not None or reduce is not None:
        reduction = _legacy_get_string(size_average, reduce)
    return _C.tp_soft_margin_loss(input, target, _get_reduction_enum(reduction))




[docs]
def cosine_embedding_loss(
    input1: Tensor,
    input2: Tensor,
    target: Tensor,
    margin: float = 0,
    size_average=None,
    reduce=None,
    reduction: str = "mean",
) -> Tensor:
    r"""Compute the cosine embedding loss.

    See :class:`~tensorplay.nn.CosineEmbeddingLoss` for details.
    """
    if size_average is not None or reduce is not None:
        reduction = _legacy_get_string(size_average, reduce)
    return _C.tp_cosine_embedding_loss(
        input1, input2, target, margin, _get_reduction_enum(reduction)
    )




[docs]
def margin_ranking_loss(
    input1: Tensor,
    input2: Tensor,
    target: Tensor,
    margin: float = 0,
    size_average=None,
    reduce=None,
    reduction: str = "mean",
) -> Tensor:
    r"""Compute the margin ranking loss.

    See :class:`~tensorplay.nn.MarginRankingLoss` for details.
    """
    if size_average is not None or reduce is not None:
        reduction = _legacy_get_string(size_average, reduce)
    return _C.tp_margin_ranking_loss(
        input1, input2, target, margin, _get_reduction_enum(reduction)
    )




[docs]
def hinge_embedding_loss(
    input: Tensor,
    target: Tensor,
    margin: float = 1.0,
    size_average=None,
    reduce=None,
    reduction: str = "mean",
) -> Tensor:
    r"""Compute the hinge embedding loss.

    See :class:`~tensorplay.nn.HingeEmbeddingLoss` for details.
    """
    if size_average is not None or reduce is not None:
        reduction = _legacy_get_string(size_average, reduce)
    return _C.tp_hinge_embedding_loss(
        input, target, margin, _get_reduction_enum(reduction)
    )




[docs]
def multi_margin_loss(
    input: Tensor,
    target: Tensor,
    p: int = 1,
    margin: float = 1.0,
    weight: Optional[Tensor] = None,
    size_average=None,
    reduce=None,
    reduction: str = "mean",
) -> Tensor:
    r"""Compute the multi margin loss, with optional weighting.

    ``sum_d max(0, margin - x_y + x_d)^p * w_y / C`` over non-target classes.
    See :class:`~tensorplay.nn.MultiMarginLoss` for details.
    """
    if size_average is not None or reduce is not None:
        reduction = _legacy_get_string(size_average, reduce)
    if p != 1 and p != 2:
        raise ValueError("only p == 1 and p == 2 supported")
    if weight is not None and weight.dim() != 1:
        raise ValueError("weight must be one-dimensional")
    return _C.multi_margin_loss(input, target.to(DType.int64), p, margin, weight,
                                _get_reduction_enum(reduction))




[docs]
def multilabel_margin_loss(
    input: Tensor,
    target: Tensor,
    size_average=None,
    reduce=None,
    reduction: str = "mean",
) -> Tensor:
    r"""Compute the multilabel margin loss.

    for each positive label ``y`` (targets are active until the first
    ``-1``), add ``max(0, 1 - x[y] + x[d])`` over non-target labels ``d``;
    divide by C.
    See :class:`~tensorplay.nn.MultiLabelMarginLoss` for details.
    """
    if size_average is not None or reduce is not None:
        reduction = _legacy_get_string(size_average, reduce)
    return _C.multilabel_margin_loss(input, target.to(DType.int64),
                                     _get_reduction_enum(reduction))





[docs]
def triplet_margin_loss(
    anchor: Tensor,
    positive: Tensor,
    negative: Tensor,
    margin: float = 1.0,
    p: float = 2,
    eps: float = 1e-6,
    swap: bool = False,
    size_average=None,
    reduce=None,
    reduction: str = "mean",
) -> Tensor:
    r"""Compute the triplet loss between given input tensors and a margin
    greater than 0.

    See :class:`~tensorplay.nn.TripletMarginLoss` for details.
    """
    if size_average is not None or reduce is not None:
        reduction = _legacy_get_string(size_average, reduce)
    if margin <= 0:
        raise ValueError(f"margin must be greater than 0, got {margin}")
    dist_pos = pairwise_distance(anchor, positive, p, eps)
    dist_neg = pairwise_distance(anchor, negative, p, eps)
    # The distance swap is described in the paper "Learning shallow
    # convolutional feature descriptors with triplet losses" (Balntas et al.).
    if swap:
        dist_swap = pairwise_distance(positive, negative, p, eps)
        dist_neg = tensorplay.minimum(dist_neg, dist_swap)
    loss = tensorplay.clamp(margin + dist_pos - dist_neg, min=0.0)
    return _apply_reduction(loss, reduction)




[docs]
def ctc_loss(
    log_probs: Tensor,
    targets: Tensor,
    input_lengths,
    target_lengths,
    blank: int = 0,
    reduction: str = "mean",
    zero_infinity: bool = False,
) -> Tensor:
    r"""Compute the Connectionist Temporal Classification loss.

    (alpha recurrence over the blank-extended target sequence); autograd flows
    through ``log_probs`` via the composed primitives.

    Args:
        log_probs: :math:`(T, N, C)` or :math:`(T, C)` log-softmax outputs.
        targets: :math:`(N, S)` or concatenated :math:`(\sum S_n,)`.
        input_lengths / target_lengths: :math:`(N,)` or scalars.
        blank: index of the blank label. Default: 0.
        reduction: ``'none' | 'mean' | 'sum'``.
        zero_infinity: zero out infinite losses (targets too long for T).
    """
    if log_probs.dim() == 2:
        log_probs = log_probs.unsqueeze(1)
        unbatched = True
    else:
        unbatched = False
    if log_probs.dim() != 3:
        raise ValueError(f"ctc_loss: log_probs must be 2D or 3D, got {log_probs.dim()}D")
    T, N, C = log_probs.shape

    def _as_len_tensor(x):
        if x is None:
            return None
        if isinstance(x, Tensor):
            return x.to(DType.int64).reshape(-1)
        if isinstance(x, int):
            return tensorplay.full([N], x, dtype=DType.int64, device=log_probs.device)
        return tensorplay.tensor(list(x), dtype=DType.int64, device=log_probs.device)

    in_lens = _as_len_tensor(input_lengths)
    tgt_lens = _as_len_tensor(target_lengths)

    if targets.dim() == 1:
        rows = []
        off = 0
        for n in range(N):
            s = int(tgt_lens[n].item())
            rows.append(tensorplay.narrow(targets.to(DType.int64), 0, off, s))
            off += s
        S = max((int(r.numel()) for r in rows), default=0)
        padded = tensorplay.zeros(N, S, dtype=DType.int64, device=log_probs.device)
        for n in range(N):
            k = rows[n].numel()
            if k:
                padded[n, :k] = rows[n]
        targets2d = padded
    elif targets.dim() == 2:
        targets2d = targets.to(DType.int64)
        S = targets2d.size(1)
    else:
        raise ValueError(f"ctc_loss: targets must be 1D or 2D, got {targets.dim()}D")

    # Native dispatch: _ctc_loss + reduction compose on the dispatcher, and
    # the _ctc_loss derivative formula (derivatives.yaml) drives the backward.
    return _ctc_loss_impl(log_probs, targets2d, in_lens, tgt_lens, blank,
                          reduction, zero_infinity, unbatched)



def _ctc_zero_inf_mask(nll):
    # impossible alignments carry +inf (or NaN from inf - inf) raw NLL
    return tensorplay.logical_or(nll.ge(float("inf")), nll.ne(nll))


def _ctc_loss_impl(log_probs, targets2d, in_lens, tgt_lens, blank, reduction,
                   zero_infinity, unbatched):
    # NB: the FASTCALL binding layer for underscore ops requires kwargs.
    nll, _ = _C._ctc_loss(log_probs=log_probs, targets=targets2d,
                          input_lengths=in_lens, target_lengths=tgt_lens,
                          blank=blank, zero_infinity=zero_infinity)
    if zero_infinity:
        nll = tensorplay.where(_ctc_zero_inf_mask(nll),
                               tensorplay.zeros_like(nll), nll)
    if reduction == "none":
        return nll.squeeze(0) if unbatched else nll
    if reduction == "sum":
        return nll.sum()
    # 'mean': divide each by its target length, then average
    denom = tgt_lens.to(nll.dtype).clamp(min=1)
    return (nll / denom).mean()




[docs]
def pixel_shuffle(input: Tensor, upscale_factor: int) -> Tensor:
    r"""Rearranges elements in a tensor of shape ``(*, C x r^2, H, W)`` to a
    tensor of shape ``(*, C, H x r, W x r)``.

    input[n, c*r^2 + i*r + j, h, w]``.
    """
    captured = _capture_call(pixel_shuffle, (input, upscale_factor), {})
    if captured is not None:
        return captured
    r = int(upscale_factor)
    if input.dim() != 4:
        raise ValueError(f"pixel_shuffle expects 4D input, got {input.dim()}D")
    N, C, H, W = input.shape
    if C % (r * r) != 0:
        raise ValueError(
            f"pixel_shuffle expects input channel to be divisible by square of upscale_factor, "
            f"but got C={C} and upscale_factor={r}")
    Cc = C // (r * r)
    x = input.reshape(N, Cc, r, r, H, W)
    x = x.permute(0, 1, 4, 2, 5, 3).contiguous()
    return x.reshape(N, Cc, H * r, W * r)




[docs]
def pixel_unshuffle(input: Tensor, downscale_factor: int) -> Tensor:
    r"""Reverses the :func:`pixel_shuffle` transformation: ``(*, C, H x r,
    W x r) -> (*, C x r^2, H, W)``."""
    captured = _capture_call(pixel_unshuffle, (input, downscale_factor), {})
    if captured is not None:
        return captured
    r = int(downscale_factor)
    if input.dim() != 4:
        raise ValueError(f"pixel_unshuffle expects 4D input, got {input.dim()}D")
    N, C, H, W = input.shape
    if H % r != 0 or W % r != 0:
        raise ValueError(
            f"pixel_unshuffle expects input height and width divisible by downscale_factor, "
            f"but got H={H}, W={W} and downscale_factor={r}")
    x = input.reshape(N, C, H // r, r, W // r, r)
    x = x.permute(0, 1, 3, 5, 2, 4).contiguous()
    return x.reshape(N, C * r * r, H // r, W // r)




[docs]
def channel_shuffle(input: Tensor, groups: int) -> Tensor:
    r"""Divide the channels in a tensor into ``g`` groups and rearrange them
    as in ShuffleNet: ``(*, C, H, W) -> (*, C, H, W)`` with channels
    interleaved across groups."""
    g = int(groups)
    if input.dim() != 4:
        raise ValueError(f"channel_shuffle expects 4D input, got {input.dim()}D")
    N, C, H, W = input.shape
    if C % g != 0:
        raise ValueError(f"channel_shuffle expects channel count divisible by groups, got C={C}, groups={g}")
    x = input.reshape(N, g, C // g, H, W)
    x = x.transpose(1, 2).contiguous()
    return x.reshape(N, C, H, W)




[docs]
def native_channel_shuffle(input: Tensor, groups: int) -> Tensor:
    return channel_shuffle(input, groups)



GRID_SAMPLE_INTERPOLATION_MODES = ('bilinear', 'nearest', 'bicubic')
GRID_SAMPLE_PADDING_MODES = ('zeros', 'border', 'reflection')


def _linspace_from_neg_one(num_steps, align_corners, dtype, device):
    """Build normalized sampling coordinates for an affine grid."""
    if num_steps <= 1:
        return tensorplay.zeros([num_steps], dtype=dtype, device=device)
    rng = tensorplay.linspace(-1, 1, num_steps, dtype=dtype, device=device)
    if not align_corners:
        rng = rng * (num_steps - 1) / num_steps
    return rng



[docs]
def affine_grid(theta: Tensor, size, align_corners=None) -> Tensor:
    r"""Generate 2D or 3D flow field (sampling grid), given a batch of affine
    matrices :attr:`theta`.

    """
    if align_corners is None:
        warnings.warn(
            "Default grid_sample and affine_grid behavior has changed "
            "to align_corners=False since 1.3.0. Please specify "
            "align_corners=True if the old behavior is desired. "
            "See the documentation of affine_grid for details.",
            stacklevel=2)
        align_corners = False
    if len(size) == 4:
        n, _, h, w = size
        if theta.dim() != 3 or theta.size(0) != n or theta.size(1) != 2 or theta.size(2) != 3:
            raise ValueError(f"affine_grid: expected theta of shape ({n}, 2, 3), got {tuple(theta.shape)}")
        dtype, dev = theta.dtype, theta.device
        xs = _linspace_from_neg_one(w, align_corners, dtype, dev).view([1, 1, w]).expand(n, h, w)
        ys = _linspace_from_neg_one(h, align_corners, dtype, dev).view([1, h, 1]).expand(n, h, w)
        ones = tensorplay.ones([n, h, w], dtype=dtype, device=dev)
        flat = tensorplay.stack([xs, ys, ones], dim=-1).reshape(n, h * w, 3)
        grid = flat.bmm(theta.transpose(1, 2))
        return grid.reshape(n, h, w, 2)
    elif len(size) == 5:
        n, _, d, h, w = size
        if theta.dim() != 3 or theta.size(0) != n or theta.size(1) != 3 or theta.size(2) != 4:
            raise ValueError(f"affine_grid: expected theta of shape ({n}, 3, 4), got {tuple(theta.shape)}")
        dtype, dev = theta.dtype, theta.device
        xs = _linspace_from_neg_one(w, align_corners, dtype, dev).view([1, 1, 1, w]).expand(n, d, h, w)
        ys = _linspace_from_neg_one(h, align_corners, dtype, dev).view([1, 1, h, 1]).expand(n, d, h, w)
        zs = _linspace_from_neg_one(d, align_corners, dtype, dev).view([1, d, 1, 1]).expand(n, d, h, w)
        ones = tensorplay.ones([n, d, h, w], dtype=dtype, device=dev)
        flat = tensorplay.stack([xs, ys, zs, ones], dim=-1).reshape(n, d * h * w, 4)
        grid = flat.bmm(theta.transpose(1, 2))
        return grid.reshape(n, d, h, w, 3)
    raise ValueError("affine_grid: size must be length 4 or 5")



def _gs_unnormalize(coord, size, align_corners):
    if align_corners:
        return ((coord + 1) / 2) * (size - 1)
    return ((coord + 1) * size - 1) / 2


def _gs_adjust(coord, size, padding_mode, align_corners=False):
    if isinstance(padding_mode, str):
        padding_mode = GRID_SAMPLE_PADDING_MODES.index(padding_mode)
    if padding_mode == 1:  # border
        return coord.clamp(0, size - 1)
    if padding_mode == 2:  # reflection
        # reflect_coordinates over [twice_low/2, twice_high/2], then border clip.
        if align_corners:
            twice_low, twice_high = 0, 2 * (size - 1)
        else:
            twice_low, twice_high = -1, 2 * size - 1
        min_ = twice_low / 2
        span = (twice_high - twice_low) / 2
        if twice_low == twice_high:
            return tensorplay.zeros_like(coord)
        c = coord - min_
        c = tensorplay.where(c < 0, -c, c)
        flips = tensorplay.floor(c / span)
        extra = c - flips * span  # == fmod(c, span); sign-safe since c >= 0
        even = (tensorplay.floor(flips / 2) * 2) == flips
        out = tensorplay.where(even, extra + min_, span - extra + min_)
        return out.clamp(0, size - 1)
    return coord


def _grid_sample_gather(input, xs, ys, in_bounds):
    """Gather input planes at integer pixel coords.

    input: (N, C, H, W); xs/ys: (N, Ho, Wo) int64; in_bounds: bool or None.
    Returns (N, C, Ho, Wo) values zeroed outside bounds when requested.
    """
    N, C, H, W = input.shape
    dev = input.device
    i64 = DType.int64
    xs_c = xs.clamp(0, W - 1)
    ys_c = ys.clamp(0, H - 1)
    pos = ys_c * W + xs_c                                  # (N, Ho, Wo)
    if in_bounds is not None:
        pos = tensorplay.where(in_bounds, pos, pos * 0)
    # keep every operand rank-4: our broadcast kernel requires equal ranks.
    bidx = tensorplay.arange(N, dtype=i64, device=dev).view([N, 1, 1, 1]) * (C * H * W)
    cidx = tensorplay.arange(C, dtype=i64, device=dev).view([1, C, 1, 1]) * (H * W)
    gid = bidx + cidx + pos.unsqueeze(1)                   # (N, C, Ho, Wo)
    # NB: use reshape, not view - our current view() binding does not record
    # autograd, silently detaching the graph (reshape does).
    vals = tensorplay.embedding(input.contiguous().reshape(-1), gid.reshape(-1)).reshape(
        [N, C, xs.shape[-2], xs.shape[-1]])
    if in_bounds is not None:
        # expand (not broadcast): our engine lacks the sum-to-shape reduction
        # propagate wrong-shaped grads to non-leaf operands mid-graph.
        vals = vals * in_bounds.unsqueeze(1).expand(
            [N, C, xs.shape[-2], xs.shape[-1]]).to(vals.dtype)
    return vals


def _grid_sampler_2d(input, grid, interpolation_mode, padding_mode, align_corners):
    if isinstance(padding_mode, str):
        padding_mode = GRID_SAMPLE_PADDING_MODES.index(padding_mode)
    N, C, H_in, W_in = input.shape
    H_out, W_out = grid.shape[1], grid.shape[2]
    x = _gs_unnormalize(grid[..., 0], W_in, align_corners)
    y = _gs_unnormalize(grid[..., 1], H_in, align_corners)
    # padding-adjusted individually instead (GridSamplerKernel.cpp
    # Bicubic::get_value_bounded -> compute_coordinates).
    if padding_mode != 0 and interpolation_mode != 2:
        x = _gs_adjust(x, W_in, padding_mode, align_corners)
        y = _gs_adjust(y, H_in, padding_mode, align_corners)

    if interpolation_mode == 1:  # nearest
        xi = tensorplay.round(x).to(DType.int64)
        yi = tensorplay.round(y).to(DType.int64)
        ib = tensorplay.logical_and(
            tensorplay.logical_and(xi >= 0, xi < W_in),
            tensorplay.logical_and(yi >= 0, yi < H_in)) if padding_mode == 0 else None
        vals = _grid_sample_gather(input, xi, yi, ib)
        if x.requires_grad:
            # composite has no such automatic path (embedding backward emits
            # gradients for weights only), so bridge an exact-zero term.
            vals = vals + (x * 0).sum()
        return vals

    x0f = tensorplay.floor(x)
    y0f = tensorplay.floor(y)
    x0 = x0f.to(DType.int64)
    y0 = y0f.to(DType.int64)
    wx = x - x0f
    wy = y - y0f

    def corner_mask(cx, cy):
        return tensorplay.logical_and(
            tensorplay.logical_and(cx >= 0, cx < W_in),
            tensorplay.logical_and(cy >= 0, cy < H_in))

    if interpolation_mode == 0:  # bilinear
        corners = [
            (x0, y0, (1 - wx) * (1 - wy)),
            (x0 + 1, y0, wx * (1 - wy)),
            (x0, y0 + 1, (1 - wx) * wy),
            (x0 + 1, y0 + 1, wx * wy),
        ]
        out = None
        for cx, cy, wgt in corners:
            ib = corner_mask(cx, cy) if padding_mode == 0 else None
            v = _grid_sample_gather(input, cx, cy, ib)
            wgt = wgt.unsqueeze(1).expand([N, C, H_out, W_out]).to(v.dtype)
            term = v * wgt
            out = term if out is None else out + term
        return out

    # Bicubic interpolation with Keys parameter alpha = -0.75.
    def _c1(t_, a=-0.75):
        return ((a + 2) * t_ - (a + 3)) * t_ * t_ + 1

    def _c2(t_, a=-0.75):
        return ((a * t_ - 5 * a) * t_ + 8 * a) * t_ - 4 * a

    tx = x - x0f
    ty = y - y0f
    wxs = [_c2(tx + 1), _c1(tx), _c1(1 - tx), _c2(2 - tx)]
    wys = [_c2(ty + 1), _c1(ty), _c1(1 - ty), _c2(2 - ty)]
    out = None
    for j in range(4):          # rows (y offset j-1)
        for k in range(4):      # cols (x offset k-1)
            cxf = x0f + (k - 1)
            cyf = y0f + (j - 1)
            if padding_mode == 2:
                # reflection: adjust each tap index (border is handled by the
                # clamp inside _grid_sample_gather; zeros masks per tap).
                cxf = _gs_adjust(cxf, W_in, 2, align_corners)
                cyf = _gs_adjust(cyf, H_in, 2, align_corners)
            cx = cxf.to(DType.int64)
            cy = cyf.to(DType.int64)
            ib = corner_mask(cx, cy) if padding_mode == 0 else None
            v = _grid_sample_gather(input, cx, cy, ib)
            wgt = (wxs[k] * wys[j]).unsqueeze(1).expand([N, C, H_out, W_out]).to(v.dtype)
            term = v * wgt
            out = term if out is None else out + term
    return out



[docs]
def grid_sample(
    input: Tensor,
    grid: Tensor,
    mode: str = "bilinear",
    padding_mode: str = "zeros",
    align_corners=None,
) -> Tensor:
    r"""Compute grid sample.

    Given an :attr:`input` and a flow-field :attr:`grid`, computes the
    ``output`` using :attr:`input` values and pixel locations from
    :attr:`grid`. Currently, only spatial (4-D) and volumetric (5-D)
    :attr:`input` are supported.

    Args:
        input (Tensor): input of shape :math:`(N, C, H_\text{in}, W_\text{in})` (4-D case)
                        or :math:`(N, C, D_\text{in}, H_\text{in}, W_\text{in})` (5-D case)
        grid (Tensor): flow-field of shape :math:`(N, H_\text{out}, W_\text{out}, 2)` (4-D case)
                       or :math:`(N, D_\text{out}, H_\text{out}, W_\text{out}, 3)` (5-D case)
        mode (str): ``'bilinear'`` | ``'nearest'`` | ``'bicubic'``. Default: ``'bilinear'``
        padding_mode (str): ``'zeros'`` | ``'border'`` | ``'reflection'``. Default: ``'zeros'``
        align_corners (bool, optional): extrema treatment, default ``False``.

    Dispatches to the native grid_sampler_2d / grid_sampler_3d kernels
    both :attr:`input` and :attr:`grid`.
    """
    if mode not in GRID_SAMPLE_INTERPOLATION_MODES:
        raise ValueError(
            f"nn.functional.grid_sample(): expected mode to be 'bilinear', 'nearest' or 'bicubic', but got: '{mode}'")
    if padding_mode not in GRID_SAMPLE_PADDING_MODES:
        raise ValueError(
            "nn.functional.grid_sample(): expected padding_mode "
            "to be 'zeros', 'border', or 'reflection', "
            f"but got: '{padding_mode}'")
    if align_corners is None:
        warnings.warn(
            "Default grid_sample and affine_grid behavior has changed "
            "to align_corners=False since 1.3.0. Please specify "
            "align_corners=True if the old behavior is desired. "
            "See the documentation of grid_sample for details.",
            stacklevel=2)
        align_corners = False
    mode_enum = GRID_SAMPLE_INTERPOLATION_MODES.index(mode)
    pad_enum = GRID_SAMPLE_PADDING_MODES.index(padding_mode)

    if input.dim() == 4:
        return _C.grid_sampler_2d(input, grid, mode_enum, pad_enum, align_corners)
    if input.dim() == 5:
        if mode_enum == 2:
            raise ValueError("nn.functional.grid_sample(): bicubic only supports 4D input")
        return _C.grid_sampler_3d(input, grid, mode_enum, pad_enum, align_corners)
    raise ValueError(f"nn.functional.grid_sample(): expected 4D or 5D input, got {input.dim()}D")



def _grid_sampler_3d(input, grid, interpolation_mode, padding_mode, align_corners):
    if isinstance(padding_mode, str):
        padding_mode = GRID_SAMPLE_PADDING_MODES.index(padding_mode)
    N, C, D_in, H_in, W_in = input.shape
    D_out, H_out, W_out = grid.shape[1], grid.shape[2], grid.shape[3]
    x = _gs_unnormalize(grid[..., 0], W_in, align_corners)
    y = _gs_unnormalize(grid[..., 1], H_in, align_corners)
    z = _gs_unnormalize(grid[..., 2], D_in, align_corners)
    if padding_mode != 0:
        x = _gs_adjust(x, W_in, padding_mode, align_corners)
        y = _gs_adjust(y, H_in, padding_mode, align_corners)
        z = _gs_adjust(z, D_in, padding_mode, align_corners)

    def gather3(xi, yi, zi, ib):
        xi_c = xi.clamp(0, W_in - 1)
        yi_c = yi.clamp(0, H_in - 1)
        zi_c = zi.clamp(0, D_in - 1)
        pos = (zi_c * H_in + yi_c) * W_in + xi_c
        if ib is not None:
            pos = tensorplay.where(ib, pos, pos * 0)
        i64 = DType.int64
        dev = input.device
        bidx = tensorplay.arange(N, dtype=i64, device=dev).view([N, 1, 1, 1, 1]) * (C * D_in * H_in * W_in)
        cidx = tensorplay.arange(C, dtype=i64, device=dev).view([1, C, 1, 1, 1]) * (D_in * H_in * W_in)
        gid = bidx + cidx + pos.unsqueeze(1)
        vals = tensorplay.embedding(input.contiguous().reshape(-1), gid.reshape(-1)).reshape(
            [N, C, D_out, H_out, W_out])
        if ib is not None:
            vals = vals * ib.unsqueeze(1).expand([N, C, D_out, H_out, W_out]).to(vals.dtype)
        return vals

    def bounds3(cx, cy, cz):
        m = tensorplay.logical_and(cx >= 0, cx < W_in)
        m = tensorplay.logical_and(m, tensorplay.logical_and(cy >= 0, cy < H_in))
        m = tensorplay.logical_and(m, tensorplay.logical_and(cz >= 0, cz < D_in))
        return m

    xf = tensorplay.floor(x)
    yf = tensorplay.floor(y)
    zf = tensorplay.floor(z)
    x0, y0, z0 = xf.to(DType.int64), yf.to(DType.int64), zf.to(DType.int64)
    wx, wy, wz = x - xf, y - yf, z - zf

    if interpolation_mode == 1:  # nearest
        xi = tensorplay.round(x).to(DType.int64)
        yi = tensorplay.round(y).to(DType.int64)
        zi = tensorplay.round(z).to(DType.int64)
        ib = bounds3(xi, yi, zi) if padding_mode == 0 else None
        vals = gather3(xi, yi, zi, ib)
        if x.requires_grad:
            # d/dgrid; bridge one so grid.grad stays defined.
            vals = vals + (x * 0).sum()
        return vals

    out = None
    for dz in (0, 1):
        for dy in (0, 1):
            for dx in (0, 1):
                cx, cy, cz = x0 + dx, y0 + dy, z0 + dz
                ib = bounds3(cx, cy, cz) if padding_mode == 0 else None
                v = gather3(cx, cy, cz, ib)
                wgt = (
                    ((wx if dx else 1 - wx) * (wy if dy else 1 - wy) * (wz if dz else 1 - wz))
                    .unsqueeze(1).expand([N, C, D_out, H_out, W_out]).to(v.dtype))
                term = v * wgt
                out = term if out is None else out + term
    return out


# -----------------------------------------------------------------------------
# Distances, embedding bags, SDPA and remaining public surface (F-alignment).
# -----------------------------------------------------------------------------


def _is_float_dtype(dt):
    return dt in (DType.float16, DType.bfloat16, DType.float32, DType.float64)


def _vector_norm(vec, p, keepdim=False):
    """Norm over the last dim of a broadcast difference, matching
"""
    dim = vec.dim() - 1
    if p == float("inf"):
        return _C.max(vec.abs(), dim=dim, keepdim=keepdim)[0]
    if p == -float("inf"):
        return -_C.max((-vec.abs()), dim=dim, keepdim=keepdim)[0]
    if p == 0:
        return vec.ne(0).to(DType.float32).sum(dim=[dim], keepdim=keepdim)
    return _C.norm(vec, [dim], float(p), keepdim)



[docs]
def pairwise_distance(x1: Tensor, x2: Tensor, p: float = 2.0, eps: float = 1e-6, keepdim: bool = False) -> Tensor:
    r"""Computes the pairwise distance between input vectors.

    dimension.
    """
    return _vector_norm(x1 - x2 + eps, p, keepdim)




[docs]
def pdist(input: Tensor, p: float = 2.0) -> Tensor:
    r"""Computes the pairwise distance between rows of :attr:`input`.

    Returns the flattened upper triangle of the ``N x N`` distance matrix —
    """
    if input.dim() != 2:
        raise RuntimeError(f"pdist expects a 2D input, got {input.dim()}D")
    n = input.size(0)
    diff = input.unsqueeze(1) - input.unsqueeze(0)      # (N, N, D)
    dist = _vector_norm(diff, p, keepdim=False)         # (N, N)
    ri = tensorplay.arange(n, dtype=DType.int64, device=input.device)
    mask = ri.view(-1, 1) < ri.view(1, -1)
    return tensorplay.masked_select(dist, mask)



def _no_grad_embedding_renorm_(weight: Tensor, input, max_norm: float, norm_type: float) -> Tensor:
    """Renormalize referenced embedding rows in-place without recording gradients."""
    with tensorplay.no_grad():
        _C.embedding_renorm_(weight, input, float(max_norm), float(norm_type))
    return weight



[docs]
def embedding_bag(
    input: Tensor,
    weight: Tensor,
    offsets=None,
    max_norm=None,
    norm_type: float = 2,
    scale_grad_by_freq: bool = False,
    mode: str = "mean",
    sparse: bool = False,
    per_sample_weights=None,
    include_last_offset: bool = False,
    padding_idx=None,
) -> Tensor:
    r"""Compute sums, means or maxes of ``bags`` of embeddings.

    1-D inputs with :attr:`offsets` (incl. ``include_last_offset``), fixed
    length 2-D inputs, ``per_sample_weights`` (sum mode), ``padding_idx``
    exclusion and ``max_norm`` renormalization.
    See :class:`tensorplay.nn.EmbeddingBag` for details.
    """
    # Backward compatibility with the old (weight, input) argument order.
    if weight.dtype == DType.int64 and _is_float_dtype(input.dtype):
        warnings.warn(
            "Argument order of nn.functional.embedding_bag was changed. "
            "Usage `embedding_bag(weight, input, ...)` is deprecated, "
            "and should now be `embedding_bag(input, weight, ...)`.",
            stacklevel=2,
        )
        weight, input = input, weight

    if per_sample_weights is not None and tuple(input.shape) != tuple(per_sample_weights.shape):
        raise ValueError(
            f"embedding_bag: If per_sample_weights ({per_sample_weights.shape}) is not None, "
            f"then it must have the same shape as the input ({input.shape})"
        )
    if weight.dim() != 2:
        raise ValueError(f"weight has to be a 2D Tensor, but got Tensor of dimension {weight.dim()}")

    if mode == "sum":
        mode_enum = 0
    elif mode == "mean":
        mode_enum = 1
    elif mode == "max":
        mode_enum = 2
        if scale_grad_by_freq:
            raise ValueError("max mode does not support scaling the gradient by the frequency")
        if sparse:
            raise ValueError("max mode does not support sparse weights")
    else:
        raise ValueError("mode has to be one of sum, mean or max")

    if max_norm is not None:
        _no_grad_embedding_renorm_(weight, input, max_norm, norm_type)

    if per_sample_weights is not None and mode != "sum":
        raise NotImplementedError(
            "embedding_bag: per_sample_weights was not None. "
            "per_sample_weights is only supported for mode='sum' "
            f"(got mode='{mode}').")

    if padding_idx is not None:
        padding_idx = int(padding_idx)
        if padding_idx >= weight.size(0) or padding_idx < -weight.size(0):
            raise ValueError(
                f"padding_idx must be within the number of embeddings ({weight.size(0)}), "
                f"got {padding_idx}")
        if padding_idx < 0:
            padding_idx += weight.size(0)
    else:
        padding_idx = -1

    i64 = DType.int64
    dev = input.device
    numel = input.numel()
    flat = input.to(i64).reshape(-1)

    include_last_offset = bool(include_last_offset)
    if input.dim() == 2:
        if offsets is not None:
            raise ValueError(
                "if input is 2D, then offsets has to be None"
                ", as input is treated is a mini-batch of"
                " fixed length sequences.")
        seq_len = int(input.size(1))
        # Fixed-length bags: one offset per row.  A zero-width row still owns a
        # bag, which arange cannot express with a zero step.
        if seq_len == 0:
            offs = tensorplay.zeros([input.size(0)], dtype=i64, device=dev)
        else:
            offs = tensorplay.arange(0, numel, seq_len, dtype=i64, device=dev)
        include_last_offset = False
    elif input.dim() == 1:
        if offsets is None:
            raise ValueError("offsets has to be a 1D Tensor but got None")
        if offsets.dim() != 1:
            raise ValueError("offsets has to be a 1D Tensor")
        offs = offsets.to(i64)
    else:
        raise ValueError(
            f"input has to be 1D or 2D Tensor, but got Tensor of dimension {input.dim()}")

    if per_sample_weights is not None:
        per_sample_weights = per_sample_weights.reshape(-1).to(weight.dtype)

    return _C._embedding_bag(
        weight, flat, offs, scale_grad_by_freq, mode_enum, sparse,
        per_sample_weights, include_last_offset, padding_idx)[0]




[docs]
def scaled_dot_product_attention(
    query: Tensor,
    key: Tensor,
    value: Tensor,
    attn_mask=None,
    dropout_p: float = 0.0,
    is_causal: bool = False,
    scale=None,
    enable_gqa: bool = False,
    backend: Optional[str] = None,
) -> Tensor:
    r"""scaled_dot_product_attention(query, key, value, attn_mask=None,
    dropout_p=0.0, is_causal=False, scale=None, enable_gqa=False,
    backend=None) -> Tensor

    Computes scaled dot product attention on query, key and value. Routes to
    reference:

    .. math::
        \text{Attention}(Q, K, V) = \text{softmax}(\frac{Q K^T}{\sqrt{E}}) V

    Args:
        backend (str, optional): ``'flash'`` | ``'mem_efficient'``,
            ``'math'``, or ``None`` to pick automatically. ``'flash'``
            selects the fused flash-attention kernel, ``'math'`` forces the
            composed reference path. When ``None``, the routing candidate
            order is governed by :func:`tensorplay.nn.attention.sdpa_kernel`.
    """
    from tensorplay.nn import attention as _sdpa_attention
    from tensorplay.overrides import has_tensorplay_function

    # Attention-bias subclasses intercept the call through the Python
    # function-hook protocol before any backend routing happens.
    if has_tensorplay_function((query, key, value, attn_mask)):
        from tensorplay.overrides import handle_tensorplay_function

        return handle_tensorplay_function(
            scaled_dot_product_attention,
            (query, key, value, attn_mask),
            query,
            key,
            value,
            attn_mask=attn_mask,
            dropout_p=dropout_p,
            is_causal=is_causal,
            scale=scale,
            enable_gqa=enable_gqa,
        )

    if attn_mask is not None and is_causal:
        raise AssertionError("Explicit attn_mask should not be set when is_causal=True")
    if query.dim() < 2 or key.dim() < 2 or value.dim() < 2:
        raise ValueError(
            "scaled_dot_product_attention: query, key and value must be "
            "at least 2-D")
    if key.shape[-1] != query.shape[-1] or value.shape[-1] != query.shape[-1]:
        raise ValueError(
            "scaled_dot_product_attention: query, key and value must share "
            "the last dimension")
    if dropout_p < 0.0 or dropout_p > 1.0:
        raise ValueError(
            f"scaled_dot_product_attention: dropout probability must be in "
            f"[0, 1], got {dropout_p}")
    if backend not in (None, "math", "flash", "mem_efficient"):
        raise ValueError(
            f"scaled_dot_product_attention: unknown backend '{backend}'; "
            "expected 'flash', 'mem_efficient', 'math' or None")

    allowed = _sdpa_attention._cur_sdpa_kernel_backends(with_priority=True)
    full_set = {
        _sdpa_attention.SDPBackend.MATH,
        _sdpa_attention.SDPBackend.FLASH_ATTENTION,
        _sdpa_attention.SDPBackend.EFFICIENT_ATTENTION,
    }
    params = _sdpa_attention.SDPAParams(
        query=query, key=key, value=value, attn_mask=attn_mask,
        dropout=dropout_p, is_causal=is_causal, enable_gqa=enable_gqa,
    )

    if backend is not None:
        _name_to_backend = {
            "math": _sdpa_attention.SDPBackend.MATH,
            "flash": _sdpa_attention.SDPBackend.FLASH_ATTENTION,
            "mem_efficient": _sdpa_attention.SDPBackend.EFFICIENT_ATTENTION,
        }
        requested = _name_to_backend[backend]
        if allowed != full_set and requested not in allowed:
            raise RuntimeError(
                f"scaled_dot_product_attention: backend '{backend}' is "
                f"disabled by the active sdpa_kernel context")
        if backend == "flash":
            plain_case = (
                scale is None
                and attn_mask is None
                and dropout_p == 0.0
                and not enable_gqa
            )
            if plain_case:
                return tensorplay.scaled_dot_product_attention(
                    query, key, value, is_causal=is_causal)
            if allowed != full_set and _sdpa_attention.SDPBackend.MATH not in allowed:
                raise RuntimeError(
                    "scaled_dot_product_attention: the requested flash backend "
                    "does not support the supplied mask, scale, or dropout "
                    "configuration")
        if backend == "mem_efficient":
            raise NotImplementedError(
                "scaled_dot_product_attention: the mem_efficient backend requires "
                "native memory-efficient attention kernels, which are not yet "
                "available in this build.")
    else:
        _plain_case = (
            scale is None
            and attn_mask is None
            and dropout_p == 0.0
            and not enable_gqa
        )
        for candidate in allowed:
            if candidate == _sdpa_attention.SDPBackend.FLASH_ATTENTION:
                if _plain_case and _sdpa_attention.can_use_flash_attention(params):
                    # impl=None lets the fused-kernel selection pick the
                    # fastest eligible kernel for the shape.
                    return tensorplay.scaled_dot_product_attention(
                        query, key, value, is_causal=is_causal)
                _sdpa_attention._raise_kernel_warnings(params)
            elif candidate == _sdpa_attention.SDPBackend.MATH:
                break
        else:
            raise RuntimeError(
                "No available kernel for scaled_dot_product_attention; "
                "the active sdpa_kernel context allows none of the "
                "installed backends for these inputs")

    out, _ = _C._scaled_dot_product_attention_math(
        query,
        key,
        value,
        attn_mask,
        float(dropout_p),
        bool(is_causal),
        None,
        scale=scale,
        enable_gqa=enable_gqa,
    )
    return out




[docs]
def linear_cross_entropy(
    input: Tensor,
    linear_weight: Tensor,
    target: Tensor,
    *,
    linear_bias=None,
    weight=None,
    reduction: str = "mean",
    ignore_index=None,
    label_smoothing: float = 0.0,
    options=None,
) -> Tensor:
    r"""Compute cross entropy between ``input``, transformed linearly, and
    target.

    Equivalent to ``cross_entropy(linear(input, linear_weight), target,
    **kwargs)`` (reference path; chunked/fused options are ignored).
    """
    if options is not None:
        warnings.warn(
            "linear_cross_entropy: ``options`` ignored; reference path used.",
            stacklevel=2,
        )
    logits = linear(input, linear_weight, linear_bias)
    ig = -100 if ignore_index is None else ignore_index
    return cross_entropy(logits, target, weight=weight, ignore_index=ig,
                         reduction=reduction, label_smoothing=label_smoothing)




[docs]
def grouped_mm(input, mat2, offs):
    return _C.grouped_mm(input, mat2, offs)




[docs]
def scaled_grouped_mm(*args, **kwargs):
    raise NotImplementedError(
        "scaled_grouped_mm requires native FP8 grouped GEMM kernels, which "
        "are not yet available in this build.")




[docs]
def scaled_mm(*args, **kwargs):
    raise NotImplementedError(
        "scaled_mm requires native FP8 GEMM kernels, which are not yet "
        "available in this build.")



# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------


def _get_softmax_dim(name: str, ndim: int, stacklevel: int = 3) -> int:
    if ndim == 0 or ndim == 1 or ndim == 3:
        ret = 0
    else:
        ret = 1
    return ret


def _mha_shape_check(query, key, value, key_padding_mask, attn_mask, num_heads):
    if query.dim() == 3:
        bsz, tgt_len, embed_dim_to_check = query.shape
        assert query.shape == (bsz, tgt_len, embed_dim_to_check)
        assert key.shape == value.shape
        bsz, src_len, _ = key.shape
        if key_padding_mask is not None:
            assert key_padding_mask.shape == (bsz, src_len)
            assert key_padding_mask.dtype == DType.bool
        if attn_mask is not None:
            if attn_mask.dim() == 2:
                correct_2d_size = (tgt_len, src_len)
                assert attn_mask.shape == correct_2d_size
            elif attn_mask.dim() == 3:
                correct_3d_size = (bsz * num_heads, tgt_len, src_len)
                assert attn_mask.shape == correct_3d_size
    elif query.dim() == 2:
        assert key.dim() == 2
        assert key.shape == value.shape
        src_len, _ = key.shape
        if key_padding_mask is not None:
            assert key_padding_mask.shape == (src_len,)
            assert key_padding_mask.dtype == DType.bool
        if attn_mask is not None:
            correct_2d_size = (src_len, src_len)
            if attn_mask.dim() == 2:
                assert attn_mask.shape == correct_2d_size
    else:
        raise AssertionError(
            f"query has to be 2d or 3d, but got {query.dim()}d")


def _in_projection(
    q,
    k,
    v,
    w_q=None,
    w_k=None,
    w_v=None,
    b_q=None,
    b_k=None,
    b_v=None,
):
    """
    projections with shape constraints ensuring embedding uniformity."""
    Eq, Ek, Ev = q.size(-1), k.size(-1), v.size(-1)
    if w_q.shape != (Eq, Eq):
        raise AssertionError(f"expecting query weights shape of {(Eq, Eq)}, but got {tuple(w_q.shape)}")
    if w_k.shape != (Eq, Ek):
        raise AssertionError(f"expecting key weights shape of {(Eq, Ek)}, but got {tuple(w_k.shape)}")
    if w_v.shape != (Eq, Ev):
        raise AssertionError(f"expecting value weights shape of {(Eq, Ev)}, but got {tuple(w_v.shape)}")
    if b_q is not None and b_q.shape != (Eq,):
        raise AssertionError(f"expecting query bias shape of {(Eq,)}, but got {tuple(b_q.shape)}")
    if b_k is not None and b_k.shape != (Eq,):
        raise AssertionError(f"expecting key bias shape of {(Eq,)}, but got {tuple(b_k.shape)}")
    if b_v is not None and b_v.shape != (Eq,):
        raise AssertionError(f"expecting value bias shape of {(Eq,)}, but got {tuple(b_v.shape)}")
    return linear(q, w_q, b_q), linear(k, w_k, b_k), linear(v, w_v, b_v)


def _in_projection_packed(q, k, v, w, b=None):
    E = q.size(-1)
    if k is v:
        if q is k:
            # self-attention: one packed projection. Reshape (not chunk) so an
            # unflatten(-1, (3, E)) validation.
            proj = linear(q, w, b)
            p = proj.reshape(tuple(proj.shape[:-1]) + (3, E))
            return tuple(p.select(-2, i).contiguous() for i in range(3))
        # encoder-decoder attention: packed kv + separate q (split is exact).
        if w.size(0) != 3 * E:
            raise RuntimeError(
                f"in_projection_packed: expected packed weight with 3*E={3 * E} "
                f"rows, got {w.size(0)}")
        w_q = w.narrow(0, 0, E)
        w_kv = w.narrow(0, E, E * 2)
        if b is None:
            b_q = b_kv = None
        else:
            b_q = b.narrow(0, 0, E)
            b_kv = b.narrow(0, E, E * 2)
        q_proj = linear(q, w_q, b_q)
        kv_proj = linear(k, w_kv, b_kv)
        p_k, p_v = tensorplay.chunk(kv_proj, 2, dim=-1)
        return q_proj, p_k.contiguous(), p_v.contiguous()
    # (3, -1), which requires the row count to be divisible by three).
    if w.size(0) % 3 != 0:
        raise RuntimeError(
            f"in_projection_packed: packed weight rows ({w.size(0)}) must be "
            f"divisible by 3")
    w_chunks = tensorplay.chunk(w, 3, dim=0)
    if b is None:
        b_q = b_k = b_v = None
    else:
        b_chunks = tensorplay.chunk(b, 3, dim=0)
        b_q, b_k, b_v = b_chunks[0], b_chunks[1], b_chunks[2]
    return (
        linear(q, w_chunks[0], b_q),
        linear(k, w_chunks[1], b_k),
        linear(v, w_chunks[2], b_v),
    )


# -----------------------------------------------------------------------------
# Pooling family (F-alignment).  Native kernels exist for 2-D pooling only;
# 3-D pools decompose into 2-D + 1-D stages (window placement factors per
# Index-returning variants reuse the native/composed values and recover
# -----------------------------------------------------------------------------


def _pool_out_size(in_size, k, s, p, d, ceil_mode):
    eff_k = d * (k - 1) + 1
    if ceil_mode:
        out = int(math.ceil((in_size + 2 * p - eff_k) / s)) + 1
        if (out - 1) * s >= in_size + p:
            out -= 1
    else:
        out = int(math.floor((in_size + 2 * p - eff_k) / s)) + 1
    return max(out, 0)


def _max_pool2d_indices(x4, kernel_size, stride, padding, dilation, oH=None, oW=None):
    """Per-plane linear indices of a 2-D max pool over ``(N, C, H, W)``.

    ``row * W + col``; first occurrence wins ties.
    """
    with tensorplay.no_grad():
        N, C, H, W = x4.shape
        kh, kw = kernel_size
        sh, sw = stride
        ph, pw = padding
        dh, dw = dilation
        dev = x4.device
        i64 = DType.int64
        if oH is None:
            oH = _pool_out_size(H, kh, sh, ph, dh, False)
        if oW is None:
            oW = _pool_out_size(W, kw, sw, pw, dw, False)

        def _rng(n):
            return tensorplay.arange(n, dtype=i64, device=dev)

        Rm = (_rng(oH) * sh).view(-1, 1, 1, 1) + (_rng(kh) * dh).view(1, 1, -1, 1) - ph
        Cm = (_rng(oW) * sw).view(1, -1, 1, 1) + (_rng(kw) * dw).view(1, 1, 1, -1) - pw
        gR = (Rm + Cm * 0).reshape(1, oH * oW, kh * kw)
        gC = (Cm + Rm * 0).reshape(1, oH * oW, kh * kw)
        valid = tensorplay.logical_and(_band(gR, 0, H), _band(gC, 0, W))

        pos = gR.clamp(0, H - 1) * W + gC.clamp(0, W - 1)
        pos = tensorplay.where(valid, pos, pos * 0)

        P = N * C
        M, K = oH * oW, kh * kw
        base = (_rng(P) * (H * W)).view(P, 1)
        gid = base + pos.reshape(1, M * K)
        vals = tensorplay.embedding(x4.contiguous().reshape(-1), gid).view(P, M, K)
        vals = tensorplay.where(valid, vals, tensorplay.full_like(vals, float("-inf")))
        # argmax over the kernel axis (last of (P, M, K)): one-hot select of
        # the winning offset per output position.
        am = _C.argmax(vals, 2, False)

        kar = _rng(K).view(1, 1, K)
        sel = am.unsqueeze(-1).eq(kar)
        pick_r = tensorplay.where(sel, gR, gR * 0).sum(-1)
        pick_c = tensorplay.where(sel, gC, gC * 0).sum(-1)
        ok = tensorplay.logical_and(_band(pick_r, 0, H), _band(pick_c, 0, W))
        idx = tensorplay.where(ok, pick_r * W + pick_c, pick_r * 0)
        return idx.view(N, C, oH, oW)



[docs]
def max_pool2d_with_indices(
    input: Tensor,
    kernel_size,
    stride=None,
    padding=0,
    dilation=1,
    ceil_mode: bool = False,
    return_indices: bool = True,
):
    r"""Applies a 2D max pooling over an input composed of several input
    planes, returning ``(output, indices)``.

    See :class:`~tensorplay.nn.MaxPool2d` for details.
    """
    kernel_size = _pair(kernel_size)
    stride = kernel_size if stride is None else _pair(stride)
    padding = _pair(padding)
    dilation = _pair(dilation)
    # int64 indices into each (n, c) input plane; autograd flows through
    # max_pool2d_with_indices_backward.
    return _C.max_pool2d_with_indices(input, list(kernel_size), list(stride),
                                      list(padding), list(dilation), ceil_mode)




[docs]
def max_pool1d_with_indices(
    input: Tensor,
    kernel_size,
    stride=None,
    padding=0,
    dilation=1,
    ceil_mode: bool = False,
    return_indices: bool = True,
):
    r"""Applies a 1D max pooling over an input signal, returning
    ``(output, indices)``.

    See :class:`~tensorplay.nn.MaxPool1d` for details.
    """
    unbatched = input.dim() == 2
    x = input.unsqueeze(0) if unbatched else input
    k = _single(kernel_size)[0]
    s = k if stride is None else _single(stride)[0]
    p = _single(padding)[0]
    d = _single(dilation)[0]
    values, indices = max_pool2d_with_indices(
        x.unsqueeze(3), (k, 1), (s, 1), (p, 0), (d, 1), ceil_mode
    )
    values = values.squeeze(3)
    indices = indices.squeeze(3)
    if unbatched:
        return values.squeeze(0), indices.squeeze(0)
    return values, indices




[docs]
def max_pool3d(
    input: Tensor,
    kernel_size,
    stride=None,
    padding=0,
    dilation=1,
    ceil_mode: bool = False,
    return_indices: bool = False,
) -> Tensor:
    r"""max_pool3d(input, kernel_size, stride=None, padding=0, dilation=1,
    ceil_mode=False, return_indices=False) -> Tensor

    Applies a 3D max pooling over an input signal composed of several input
    planes. Input shape ``(N, C, D, H, W)`` or unbatched ``(C, D, H, W)``.
    """
    captured = _capture_call(max_pool3d, (input, kernel_size, stride, padding, dilation, ceil_mode, return_indices), {})
    if captured is not None:
        return captured
    if return_indices:
        return max_pool3d_with_indices(
            input, kernel_size, stride=stride, padding=padding,
            dilation=dilation, ceil_mode=ceil_mode)
    kd, kh, kw = _triple(kernel_size)
    sd, sh, sw = _triple(stride) if stride is not None else (kd, kh, kw)
    pd_, ph, pw = _triple(padding)
    dd, dh, dw = _triple(dilation)
    # max_pool3d_backward.
    return _C.max_pool3d(input, [kd, kh, kw], [sd, sh, sw], [pd_, ph, pw],
                         [dd, dh, dw], ceil_mode)




[docs]
def max_pool3d_with_indices(
    input: Tensor,
    kernel_size,
    stride=None,
    padding=0,
    dilation=1,
    ceil_mode: bool = False,
    return_indices: bool = True,
):
    r"""Applies a 3D max pooling over an input signal, returning
    ``(output, indices)``.

    See :class:`~tensorplay.nn.MaxPool3d` for details.
    """
    kd, kh, kw = _triple(kernel_size)
    sd, sh, sw = _triple(stride) if stride is not None else (kd, kh, kw)
    pd_, ph, pw = _triple(padding)
    dd, dh, dw = _triple(dilation)
    # Native kernel returns values plus int64 indices into each (n, c) input
    # (D, H, W) volume; autograd flows through max_pool3d_with_indices_backward.
    return _C.max_pool3d_with_indices(input, [kd, kh, kw], [sd, sh, sw],
                                      [pd_, ph, pw], [dd, dh, dw], ceil_mode)




[docs]
def avg_pool3d(
    input: Tensor,
    kernel_size,
    stride=None,
    padding=0,
    ceil_mode: bool = False,
    count_include_pad: bool = True,
    divisor_override=None,
) -> Tensor:
    r"""avg_pool3d(input, kernel_size, stride=None, padding=0, ceil_mode=False,
    count_include_pad=True, divisor_override=None) -> Tensor

    Applies a 3D average pooling over an input signal composed of several
    input planes. Input shape ``(N, C, D, H, W)`` or unbatched ``(C, D, H, W)``.
    """
    captured = _capture_call(avg_pool3d, (input, kernel_size, stride, padding, ceil_mode, count_include_pad, divisor_override), {})
    if captured is not None:
        return captured
    unbatched = input.dim() == 4
    x = input.unsqueeze(0) if unbatched else input
    kd, kh, kw = _triple(kernel_size)
    if stride is None:
        sd, sh, sw = kd, kh, kw
    else:
        sd, sh, sw = _triple(stride)
    pd_, ph, pw = _triple(padding)
    output = _C.avg_pool3d(x, (kd, kh, kw), (sd, sh, sw), (pd_, ph, pw),
                            ceil_mode, count_include_pad, divisor_override)
    return output.squeeze(0) if unbatched else output




[docs]
def adaptive_avg_pool3d(input: Tensor, output_size) -> Tensor:
    r"""Apply a 3D adaptive average pooling over an input signal."""
    unbatched = input.dim() == 4
    x = input.unsqueeze(0) if unbatched else input
    od, oh, ow = _triple(output_size)
    return _C.adaptive_avg_pool3d(x, (od, oh, ow)).squeeze(0) if unbatched \
        else _C.adaptive_avg_pool3d(x, (od, oh, ow))




[docs]
def lp_pool3d(
    input: Tensor,
    norm_type,
    kernel_size,
    stride=None,
    ceil_mode: bool = False,
) -> Tensor:
    r"""Apply a 3D power-average pooling over an input signal.

    See :class:`~tensorplay.nn.LPPool3d` for details.
    """
    kd, kw, kh = _triple(kernel_size)
    if isinstance(norm_type, (int, float)):
        if norm_type == 0:
            raise ValueError(f"norm_type must be a non-zero value, but got {norm_type}")
        if norm_type == float("inf"):
            return max_pool3d(input.abs(), kernel_size, stride, 0, 1, ceil_mode)
        if norm_type == -float("inf"):
            return -max_pool3d((-input.abs()), kernel_size, stride, 0, 1, ceil_mode)

    if stride is not None:
        out = avg_pool3d(input.pow(norm_type), kernel_size, stride, 0, ceil_mode)
    else:
        out = avg_pool3d(input.pow(norm_type), kernel_size, padding=0, ceil_mode=ceil_mode)

    return (tensorplay.sign(out) * relu(tensorplay.abs(out))).mul(kd * kw * kh).pow(1.0 / norm_type)



def _adaptive_window_bounds(in_size, out_size):
    starts = [i * in_size // out_size for i in range(out_size)]
    ends = [-((-(i + 1) * in_size) // out_size) for i in range(out_size)]
    return starts, ends


def _adaptive_max_pool2d_wi(x4, oH, oW):
    """Values + indices for adaptive 2-D max pooling (loop over cells)."""
    with tensorplay.no_grad():
        N, C, H, W = x4.shape
        hs_list, he_list = _adaptive_window_bounds(H, oH)
        ws_list, we_list = _adaptive_window_bounds(W, oW)
        vals, idxs = [], []
        for i in range(oH):
            for j in range(oW):
                win = x4[:, :, hs_list[i]:he_list[i], ws_list[j]:we_list[j]].contiguous()
                a = he_list[i] - hs_list[i]
                b = we_list[j] - ws_list[j]
                t = win.reshape(N, C, a, b)
                cv, ci = _C.topk(t, 1, -1, True, True, 0)
                cv = cv.view(N, C, a, 1)
                ci = ci.view(N, C, a, 1)
                # topk kernel is last-dim only: swap the window axis to last.
                rv, ri = _C.topk(cv.transpose(3, 2), 1, -1, True, True, 0)
                ri = ri.view(N, C)
                sel = ri.view(N, C, 1, 1).eq(
                    tensorplay.arange(a, dtype=DType.int64,
                                      device=x4.device).view(1, 1, -1, 1))
                col = tensorplay.where(sel, ci, ci * 0).sum(2).view(N, C)
                vals.append(rv.view(N, C))
                idxs.append((hs_list[i] + ri) * W + (ws_list[j] + col))
        v = tensorplay.stack(vals, dim=2).reshape(N, C, oH, oW)
        ix = tensorplay.stack(idxs, dim=2).reshape(N, C, oH, oW)
        return v, ix



[docs]
def adaptive_max_pool2d_with_indices(input: Tensor, output_size, return_indices: bool = True):
    r"""Applies a 2D adaptive max pooling over an input signal composed of
    several input planes, returning ``(output, indices)``.

    See :class:`~tensorplay.nn.AdaptiveMaxPool2d` for details.
    """
    output_size = list(_pair(output_size))
    unbatched = input.dim() == 3
    x = input.unsqueeze(0) if unbatched else input
    values, indices = _C.adaptive_max_pool2d_with_indices(x, output_size)
    if unbatched:
        return values.squeeze(0), indices.squeeze(0)
    return values, indices




[docs]
def adaptive_max_pool1d_with_indices(input: Tensor, output_size, return_indices: bool = True):
    r"""Applies a 1D adaptive max pooling over an input signal, returning
    ``(output, indices)``.

    See :class:`~tensorplay.nn.AdaptiveMaxPool1d` for details.
    """
    unbatched = input.dim() == 2
    x = input.unsqueeze(0) if unbatched else input
    values, indices = adaptive_max_pool2d_with_indices(x.unsqueeze(3), (output_size, 1))
    values = values.squeeze(3)
    indices = indices.squeeze(3)
    if unbatched:
        return values.squeeze(0), indices.squeeze(0)
    return values, indices



def _adaptive_max_values_3d(x5, od, oh, ow):
    N, C, D, H, W = x5.shape
    hs, he = _adaptive_window_bounds(D, od)
    vs, ixs = [], []
    for d in range(od):
        dsz = he[d] - hs[d]
        sl = x5[:, :, hs[d]:he[d], :, :].reshape(N * C * dsz, 1, H, W)
        pv = _C.adaptive_max_pool2d(sl, [oh, ow]).reshape(N, C, dsz, oh, ow)
        _, pi = _adaptive_max_pool2d_wi(sl.reshape(N * C * dsz, H, W).unsqueeze(1), oh, ow)
        pi = pi.reshape(N, C, dsz, oh, ow)
        # max is associative: reduce the depth window after pooling (H, W).
        vs.append(_C.max(pv, dim=2)[0])
        # argmax kernel mishandles non-last dims: bring dsz to the end.
        zt = pv.transpose(2, 4).contiguous().reshape(N * C * oh * ow, dsz)
        _, zt_idx = _C.topk(zt, 1, -1, True, True, 0)
        # rows are (n, c, w_out, h_out) after the transpose: undo it.
        z = zt_idx.reshape(N, C, ow, oh).transpose(2, 3)
        sel = z.unsqueeze(2).eq(tensorplay.arange(dsz, dtype=DType.int64,
                                                 device=x5.device).view(1, 1, -1, 1, 1))
        win_idx = (pi * sel).sum(2) + (hs[d] + z) * (H * W)
        ixs.append(win_idx)
    return tensorplay.stack(vs, dim=2), tensorplay.stack(ixs, dim=2)



[docs]
def adaptive_max_pool3d(input: Tensor, output_size, return_indices: bool = False):
    r"""adaptive_max_pool3d(input, output_size, return_indices=False)

    Applies a 3D adaptive max pooling over an input signal composed of several
    input planes. Input shape ``(N, C, D, H, W)`` or unbatched ``(C, D, H, W)``.

    See :class:`~tensorplay.nn.AdaptiveMaxPool3d` for details.
    """
    od, oh, ow = _triple(output_size)
    if return_indices:
        return adaptive_max_pool3d_with_indices(input, (od, oh, ow))
    # through adaptive_max_pool3d_backward.
    return _C.adaptive_max_pool3d(input, [od, oh, ow])




[docs]
def adaptive_max_pool3d_with_indices(input: Tensor, output_size, return_indices: bool = True):
    r"""Applies a 3D adaptive max pooling over an input signal, returning
    ``(output, indices)``.

    See :class:`~tensorplay.nn.AdaptiveMaxPool3d` for details.
    """
    od, oh, ow = _triple(output_size)
    unbatched = input.dim() == 4
    x = input.unsqueeze(0) if unbatched else input
    N, C, D, H, W = x.shape
    with tensorplay.no_grad():
        _values, indices = _adaptive_max_values_3d(x, od, oh, ow)
    # Values come from the native kernel so autograd flows through
    # adaptive_max_pool3d_backward; indices stay a no-grad int64 tensor.
    values = _C.adaptive_max_pool3d(x, [od, oh, ow])
    if unbatched:
        return values.squeeze(0), indices.squeeze(0)
    return values, indices



# -----------------------------------------------------------------------------
# generate_intervals + FractionalMaxPool{2d,3d}.cpp window scan, vectorized
# through an embedding gather so autograd flows to the input).
# -----------------------------------------------------------------------------


def _frac_generate_intervals(sample, in_size, out_size, pool_size):
    """

    ``sample`` is a ``(P,)`` float tensor; returns ``(P, out_size)`` int64
    window start positions.
    """
    dev = sample.device
    last = in_size - pool_size
    P = sample.numel()
    if out_size <= 1:
        return tensorplay.full([P, max(out_size, 0)], last, dtype=DType.int64, device=dev)
    alpha = float(in_size - pool_size) / float(out_size - 1)
    i = tensorplay.arange(out_size - 1, dtype=sample.dtype, device=dev)
    # static_cast<int> truncates toward zero; operands are non-negative here
    seq = ((i.unsqueeze(0) + sample.unsqueeze(1)) * alpha).to(DType.int64)
    seq = seq - (sample * alpha).to(DType.int64).unsqueeze(1)
    tail = tensorplay.full([P, 1], last, dtype=DType.int64, device=dev)
    return tensorplay.cat([seq, tail], dim=1)


def _frac_pool_check(input, _random_samples, ndim_spatial):
    if _random_samples.dim() != 3:
        raise ValueError(f"Expect _random_samples to have 3 dimensions, got {_random_samples.dim()}")
    nbatch = 1 if input.dim() == ndim_spatial + 1 else input.size(0)
    channels = input.size(0) if input.dim() == ndim_spatial + 1 else input.size(1)
    if _random_samples.size(0) < nbatch:
        raise ValueError("Expect _random_samples.size(0) no less then input batch size.")
    if _random_samples.size(1) != channels:
        raise ValueError("Expect _random_samples.size(1) equals to input channel size.")
    if _random_samples.size(2) != ndim_spatial:
        raise ValueError(f"Expect _random_samples.size(2) equals to {ndim_spatial}; got {_random_samples.size(2)}.")


def _frac_windowed_max(x_planes, pos, plane_size):
    """Max + argmax of windows gathered per-plane through an embedding lookup.

    x_planes: ``(P, V)``; pos: ``(P, M, K)`` int64 within-plane positions.
    ``val > maxVal || isnan(val)`` scan.
    """
    P = x_planes.size(0)
    M, K = pos.shape[1], pos.shape[2]
    base = (tensorplay.arange(P, dtype=DType.int64, device=x_planes.device) * plane_size).view(P, 1)
    gid = base + pos.reshape(P, M * K)
    vals = tensorplay.embedding(x_planes.contiguous().reshape(-1), gid).view(P, M, K)
    sub = tensorplay.where(vals.ne(vals), tensorplay.full_like(vals, float("inf")), vals)
    am = _C.argmax(sub, 2, False)  # window-max index along K
    kar = tensorplay.arange(K, dtype=DType.int64, device=x_planes.device).view(1, 1, K)
    sel = am.unsqueeze(-1).eq(kar)
    picked = tensorplay.where(sel, vals, vals * 0).sum(-1)
    idx = tensorplay.where(sel, pos, pos * 0).sum(-1)
    return picked, idx



[docs]
def fractional_max_pool2d_with_indices(
    input: Tensor,
    kernel_size,
    output_size=None,
    output_ratio=None,
    return_indices: bool = True,
    _random_samples=None,
):
    r"""Applies 2D fractional max pooling over an input signal composed of
    several input planes, returning ``(output, indices)``.

    The max-pooling operation is applied in :math:`kH \times kW` regions by a
    stochastic step size determined by the target output size.
    The number of output features is equal to the number of input planes.

    Args:
        kernel_size: the size of the window, ``k`` or ``(kH, kW)``
        output_size: target output size ``oH x oW``
        output_ratio: alternative to output_size, in range (0, 1)
        return_indices: return pooling indices as well
        _random_samples: optional ``(B, C, 2)`` random starts override

    See :class:`~tensorplay.nn.FractionalMaxPool2d` for details.
    """
    if output_size is None and output_ratio is None:
        raise ValueError("fractional_max_pool2d requires specifying either an output_size or an output_ratio")
    if output_size is None:
        _output_ratio = _pair(output_ratio)
        output_size = [int(input.size(-2) * _output_ratio[0]), int(input.size(-1) * _output_ratio[1])]
    output_size = list(_pair(output_size))
    kh, kw = _pair(kernel_size)
    oH, oW = int(output_size[0]), int(output_size[1])

    unbatched = input.dim() == 3
    x = input.unsqueeze(0) if unbatched else input
    B, C, H, W = x.shape
    if oH < 1 or oW < 1 or kh < 1 or kw < 1:
        raise ValueError(
            f"fractional_max_pool2d: kernel_size ({kh}, {kw}) and output_size ({oH}, {oW}) must be positive")
    if oH + kh - 1 > H or oW + kw - 1 > W:
        raise ValueError(
            f"fractional_max_pool2d: output_size ({oH}, {oW}) too large relative to "
            f"input ({H}, {W}) and kernel ({kh}, {kw})")

    if _random_samples is None:
        _random_samples = tensorplay.rand(B, C, 2, dtype=input.dtype, device=input.device)
    _frac_pool_check(x, _random_samples, 2)

    # intervals derive from _random_samples, indices are flat in-plane offsets.
    values, indices = _C.fractional_max_pool2d(x, [kh, kw], [oH, oW], _random_samples)
    if unbatched:
        return values.squeeze(0), indices.squeeze(0)
    return values, indices



def _fractional_max_pool2d(
    input: Tensor,
    kernel_size,
    output_size=None,
    output_ratio=None,
    return_indices: bool = False,
    _random_samples=None,
) -> Tensor:
    return fractional_max_pool2d_with_indices(
        input, kernel_size, output_size=output_size, output_ratio=output_ratio,
        return_indices=return_indices, _random_samples=_random_samples)[0]



[docs]
def fractional_max_pool2d(
    input: Tensor,
    kernel_size,
    output_size=None,
    output_ratio=None,
    return_indices: bool = False,
    _random_samples=None,
):
    r"""Applies 2D fractional max pooling over an input signal.

    If :attr:`return_indices` is ``True``, returns ``(output, indices)``;
    otherwise just the output.
    """
    if return_indices:
        return fractional_max_pool2d_with_indices(
            input, kernel_size, output_size=output_size, output_ratio=output_ratio,
            return_indices=True, _random_samples=_random_samples)
    return _fractional_max_pool2d(
        input, kernel_size, output_size=output_size, output_ratio=output_ratio,
        _random_samples=_random_samples)




[docs]
def fractional_max_pool3d_with_indices(
    input: Tensor,
    kernel_size,
    output_size=None,
    output_ratio=None,
    return_indices: bool = True,
    _random_samples=None,
):
    r"""Applies 3D fractional max pooling over an input signal composed of
    several input planes, returning ``(output, indices)``.

    Each plane consumes three random samples ordered ``(T, H, W)``, matching
    """
    if output_size is None and output_ratio is None:
        raise ValueError("fractional_max_pool3d requires specifying either an output_size or an output_ratio")
    if output_size is None:
        _output_ratio = _triple(output_ratio)
        output_size = [
            int(input.size(-3) * _output_ratio[0]),
            int(input.size(-2) * _output_ratio[1]),
            int(input.size(-1) * _output_ratio[2]),
        ]
    output_size = list(_triple(output_size))
    kt, kh, kw = _triple(kernel_size)
    oT, oH, oW = int(output_size[0]), int(output_size[1]), int(output_size[2])

    unbatched = input.dim() == 4
    x = input.unsqueeze(0) if unbatched else input
    B, C, T, H, W = x.shape
    if min(oT, oH, oW) < 1 or min(kt, kh, kw) < 1:
        raise ValueError("fractional_max_pool3d: kernel_size and output_size must be positive")
    if oT + kt - 1 > T or oH + kh - 1 > H or oW + kw - 1 > W:
        raise ValueError(
            f"fractional_max_pool3d: output_size ({oT}, {oH}, {oW}) too large relative to "
            f"input ({T}, {H}, {W}) and kernel ({kt}, {kh}, {kw})")

    if _random_samples is None:
        _random_samples = tensorplay.rand(B, C, 3, dtype=input.dtype, device=input.device)
    _frac_pool_check(x, _random_samples, 3)

    # samples ordered (T, H, W), indices flat in-plane offsets.
    values, indices = _C.fractional_max_pool3d(x, [kt, kh, kw], [oT, oH, oW], _random_samples)
    if unbatched:
        return values.squeeze(0), indices.squeeze(0)
    return values, indices



def _fractional_max_pool3d(
    input: Tensor,
    kernel_size,
    output_size=None,
    output_ratio=None,
    return_indices: bool = False,
    _random_samples=None,
) -> Tensor:
    return fractional_max_pool3d_with_indices(
        input, kernel_size, output_size=output_size, output_ratio=output_ratio,
        return_indices=return_indices, _random_samples=_random_samples)[0]



[docs]
def fractional_max_pool3d(
    input: Tensor,
    kernel_size,
    output_size=None,
    output_ratio=None,
    return_indices: bool = False,
    _random_samples=None,
):
    r"""Applies 3D fractional max pooling over an input signal.

    If :attr:`return_indices` is ``True``, returns ``(output, indices)``;
    otherwise just the output.
    """
    if return_indices:
        return fractional_max_pool3d_with_indices(
            input, kernel_size, output_size=output_size, output_ratio=output_ratio,
            return_indices=True, _random_samples=_random_samples)
    return _fractional_max_pool3d(
        input, kernel_size, output_size=output_size, output_ratio=output_ratio,
        _random_samples=_random_samples)



# -----------------------------------------------------------------------------
# Max unpooling (partial inverse of max pooling).  Composed as a scatter of
# the pooled values into a zero canvas via the differentiable index_add op.
# -----------------------------------------------------------------------------


def _unpool_output_size(
    input: Tensor,
    kernel_size,
    stride,
    padding,
    output_size,
):
    input_size = input.size()
    n = len(kernel_size)
    default_size = [
        (input_size[-n + d] - 1) * stride[d] + kernel_size[d] - 2 * padding[d]
        for d in range(n)
    ]
    if output_size is None:
        ret = default_size
    else:
        if len(output_size) == n + 2:
            output_size = output_size[2:]
        if len(output_size) != n:
            raise ValueError(
                "output_size should be a sequence containing "
                f"{n} or {n + 2} elements, but it has a length of '{len(output_size)}'"
            )
        ret = list(output_size)
        for d in range(n):
            min_size = default_size[d] - stride[d]
            max_size = default_size[d] + stride[d]
            if not (min_size < ret[d] < max_size):
                raise ValueError(
                    f'invalid output_size "{output_size}" (dim {d} must be between {min_size} and {max_size})'
                )
    for d in range(n):
        if ret[d] < 0:
            raise ValueError(
                "max_unpooling: output_size must contain non-negative spatial "
                f"dimensions, but got output_size[{d}]={ret[d]}"
            )
    return ret



[docs]
def max_unpool1d(
    input: Tensor,
    indices: Tensor,
    kernel_size,
    stride=None,
    padding=0,
    output_size=None,
) -> Tensor:
    r"""Compute a partial inverse of :class:`MaxPool1d`.

    See :class:`~tensorplay.nn.MaxUnpool1d` for details.
    """
    kernel_size = _single(kernel_size)
    _stride = _single(stride) if stride is not None else kernel_size
    padding = _single(padding)
    output_size = _unpool_output_size(input, kernel_size, _stride, padding, output_size)
    return _C.max_unpool2d(
        input.unsqueeze(-1), indices.unsqueeze(-1), list(output_size) + [1]
    ).squeeze(-1)




[docs]
def max_unpool2d(
    input: Tensor,
    indices: Tensor,
    kernel_size,
    stride=None,
    padding=0,
    output_size=None,
) -> Tensor:
    r"""Compute a partial inverse of :class:`MaxPool2d`.

    See :class:`~tensorplay.nn.MaxUnpool2d` for details.
    """
    kernel_size = _pair(kernel_size)
    _stride = _pair(stride) if stride is not None else kernel_size
    padding = _pair(padding)
    output_size = _unpool_output_size(input, kernel_size, _stride, padding, output_size)
    # pooled values into a zero canvas at the flat in-plane int64 indices.
    return _C.max_unpool2d(input, indices, list(output_size))




[docs]
def max_unpool3d(
    input: Tensor,
    indices: Tensor,
    kernel_size,
    stride=None,
    padding=0,
    output_size=None,
) -> Tensor:
    r"""Compute a partial inverse of :class:`MaxPool3d`.

    See :class:`~tensorplay.nn.MaxUnpool3d` for details.
    """
    kernel_size = _triple(kernel_size)
    _stride = _triple(stride) if stride is not None else kernel_size
    padding = _triple(padding)
    output_size = _unpool_output_size(input, kernel_size, _stride, padding, output_size)
    return _C.max_unpool3d(input, indices, list(output_size), list(_stride), list(padding))

Search documentation

Search all 1,743 documentation pages.

Keyboard shortcuts

Global

  • /Focus search
  • ?This dialog
  • ,Open settings
  • jAI assistant

Search

  • Navigate results
  • Open result
  • escClose

Package

  • mMain information
  • dDocs
  • .Code
  • -Changelog
  • tTimeline
  • sStats
  • vVersions