TensorPlay
latest (dev)
Copy
View Markdown

Latest development documentation · Updated 2026-10-15

Source code for tensorplay._composite_funcs

"""

Complements ``functional.py`` (codegen) and ``_shape_funcs.py``. Every
public name here is expressed over existing differentiable primitives
(mm/cat/narrow/gather/index_select/pow/sort/searchsorted/bincount/...),
so autograd flows through composition wherever the underlying ops are
``test/test_composite_funcs.py``.

Deliberate narrows (documented per function):
- conjugation-bit queries report physical layout (tp has no conj bit);
- ``cartesian_prod`` accepts 1-D inputs.
"""

import itertools
import math

import numpy as np

import tensorplay
from tensorplay._C import DType
from tensorplay.autograd.function import Function
from tensorplay.graph import capture_call as _capture_call

__all__ = [
    "absolute", "arccos", "arccosh", "arcsin", "arcsinh", "arctan",
    "arctanh", "arctan2",
    "acos_", "asin_", "atan_", "acosh_", "asinh_", "atanh_",
    "concat", "concatenate", "ger", "adjoint",
    "detach", "diagflat", "numel", "scalar_tensor",
    "chain_matmul", "matrix_power", "kron", "vander",
    "tril_indices", "triu_indices", "cartesian_prod", "combinations",
    "trapezoid", "trapz", "cumulative_trapezoid",
    "quantile", "nanquantile", "histc", "histogram",
    "isin", "unique_consecutive", "repeat_interleave", "kaiser_window",
    "lstm_cell", "rnn_relu_cell", "rnn_tanh_cell",
    "put", "resolve_conj", "resolve_neg", "is_conj", "is_neg",
    "can_cast", "promote_types", "result_type", "is_nonzero",
    "is_same_size", "get_device",
    "alias_copy", "t_copy", "permute_copy", "transpose_copy",
    "squeeze_copy", "unsqueeze_copy", "select_copy", "slice_copy",
    "narrow_copy", "diagonal_copy", "unbind_copy", "split_copy",
    "view_copy", "unfold_copy", "expand_copy", "reshape_as",
    "unsafe_chunk", "unsafe_split",
    "rms_norm", "cosine_similarity",
    "max_pool1d", "avg_pool1d", "adaptive_avg_pool1d",
    "adaptive_max_pool1d",
    "dropout", "dropout_", "alpha_dropout", "feature_dropout",
    "feature_dropout_", "feature_alpha_dropout", "rrelu", "rrelu_",
    "bilinear", "ctc_loss", "embedding_bag", "conv_tbc",
    "max_pool3d", "max_pool1d_with_indices", "native_channel_shuffle",
]


# ---------------------------------------------------------------------------
# helpers
# ---------------------------------------------------------------------------

def _norm_dim(dim, ndim):
    d = int(dim)
    if d < 0:
        d += ndim
    if d < 0 or d >= ndim:
        raise IndexError(
            f"Dimension out of range (expected to be in range of "
            f"[{-ndim}, {ndim - 1}], but got {dim})"
        )
    return d


def _as_tensor(x):
    if isinstance(x, tensorplay.Tensor):
        return x
    return tensorplay.as_tensor(x)


def _band(a, b):
    return tensorplay.logical_and(a, b)


def _slice_getitem(t, dim, start, end, step=1):
    sl = [slice(None)] * t.dim()
    sl[dim] = slice(int(start), int(end), int(step))
    return t[tuple(sl)]


def _make_arc_inplace(base, deriv):
    class _ArcInplaceFn(Function):
        @staticmethod
        def forward(ctx, self):
            ctx.pre = self.clone()
            res = getattr(self, base)()
            with tensorplay.no_grad():
                self.copy_(res)
            return self

        @staticmethod
        def backward(ctx, grad_output):
            return (grad_output * deriv(ctx.pre),)

    def op(self):
        return _ArcInplaceFn.apply(self)

    op.__name__ = base + "_"
    return op


def _d_acos(x):
    return tensorplay.full_like(x, 1.0).sub_(x.mul(x)).rsqrt().neg()


def _d_asin(x):
    return tensorplay.full_like(x, 1.0).sub_(x.mul(x)).rsqrt()


def _d_atan(x):
    return x.mul(x).add(1.0).reciprocal()


def _d_acosh(x):
    return x.mul(x).sub(1.0).rsqrt()


def _d_asinh(x):
    return x.mul(x).add(1.0).rsqrt()


def _d_atanh(x):
    return x.neg().mul(x).add(1.0).reciprocal()


acos_ = _make_arc_inplace("acos", _d_acos)
asin_ = _make_arc_inplace("asin", _d_asin)
atan_ = _make_arc_inplace("atan", _d_atan)
acosh_ = _make_arc_inplace("acosh", _d_acosh)
asinh_ = _make_arc_inplace("asinh", _d_asinh)
atanh_ = _make_arc_inplace("atanh", _d_atanh)


# ---------------------------------------------------------------------------
# aliases / semantic wrappers
# ---------------------------------------------------------------------------

def absolute(input):
    """Alias of :func:`abs`."""
    return input.abs()


def arccos(input):
    return input.acos()


def arcsin(input):
    return input.asin()


def arctan(input):
    return input.atan()


def arccosh(input):
    return input.acosh()


def arcsinh(input):
    return input.asinh()


def arctanh(input):
    return input.atanh()


def arctan2(input, other):
    """atan2(y, x) = 2*atan(y / (hypot + x)); native atan2 CPU kernel is
    currently unregistered in this tree, so compose. Edge narrows vs
"""
    y = input if isinstance(input, tensorplay.Tensor) else _as_tensor(input)
    x = other if isinstance(other, tensorplay.Tensor) else _as_tensor(other)
    xd = x.to(DType.float64)
    yd = y.to(DType.float64)
    r = xd.mul(xd).add(yd.mul(yd)).sqrt()
    den = r.add(xd)
    den_safe = tensorplay.where(
        den.eq(0), tensorplay.full_like(den, 1e-300), den)
    base = yd.div(den_safe).atan().mul(2.0)
    neg_half_pi = -math.pi / 2.0
    left = tensorplay.where(
        yd.lt(0),
        tensorplay.full_like(base, neg_half_pi),
        tensorplay.full_like(base, math.pi / 2.0),
    )
    out = tensorplay.where(_band(xd.lt(0), yd.eq(0)), left, base)
    od = _float_out_dtype(input.dtype)
    return out.to(od)


def concat(tensors, dim=0, *, out=None):
    result = tensorplay.cat(tensors=list(tensors), dim=dim)
    if out is not None:
        out.copy_(result)
        return out
    return result


def concatenate(tensors, dim=0, *, out=None):
    return concat(tensors, dim=dim, out=out)


def ger(input, vec2):
    return input.outer(vec2)


def adjoint(input):
    out = input.transpose(-2, -1)
    if input.dtype in (DType.complex64, DType.complex128,
                       DType.bcomplex32, DType.complex32):
        out = tensorplay.conj(out)
    return out


def detach(input):
    return input.detach()


def diagflat(input, offset=0):
    return input.reshape([-1]).diag(offset)


def numel(obj):
    return obj.numel()


def scalar_tensor(s, *, dtype=None, device=None):
    t = tensorplay.tensor([s], dtype=dtype, device=device)
    return t.reshape([])


# ---------------------------------------------------------------------------
# linear algebra / structured
# ---------------------------------------------------------------------------

def chain_matmul(*matrices):
    if len(matrices) == 0:
        raise RuntimeError("chain_matmul(): expected at least one matrix")
    out = matrices[0]
    for m in matrices[1:]:
        out = out.mm(m)
    return out


def matrix_power(input, n):
    n = int(n)
    if input.dim() != 2:
        raise RuntimeError("matrix_power(): expected a 2-D square matrix")
    if input.size(0) != input.size(1):
        raise RuntimeError("matrix_power(): expected a square matrix")
    result = tensorplay.eye(input.size(0), dtype=input.dtype,
                            device=input.device)
    base = tensorplay.linalg.inv(input) if n < 0 else input
    e = -n if n < 0 else n
    while e > 0:
        if e & 1:
            result = result.mm(base)
        e >>= 1
        if e:
            base = base.mm(base)
    return result


def kron(input, other):
    a = input if input.dim() >= 1 else input.reshape([1])
    b = other if other.dim() >= 1 else other.reshape([1])
    if a.dim() != b.dim():
        raise RuntimeError(
            f"kron(): number of dimensions must match, got {a.dim()} "
            f"and {b.dim()}"
        )
    ndim = a.dim()
    ashape = list(a.size())
    bshape = list(b.size())
    pa = []
    pb = []
    merged = []
    for s_a, s_b in zip(ashape, bshape):
        pa.extend([s_a, 1])
        pb.extend([1, s_b])
        merged.append(s_a * s_b)
    out = a.reshape(pa).mul(b.reshape(pb))
    return out.reshape(merged)


def vander(x, N=None, increasing=False):
    if x.dim() != 1:
        raise RuntimeError("vander(): expected a 1-D input tensor")
    n = x.numel() if N is None else int(N)
    if n < 0:
        raise ValueError("vander(): N must be non-negative")
    exps = tensorplay.arange(n, device=x.device)
    if not increasing:
        exps = tensorplay.flip(exps, dims=[0])
    return x.unsqueeze(1).pow(exps.unsqueeze(0))


def tril_indices(row, col, offset=0, *, dtype=DType.int64, device=None,
                 pin_memory=False):
    row, col, offset = int(row), int(col), int(offset)
    if row < 0 or col < 0:
        raise ValueError("tril_indices(): row and col must be non-negative")
    rr = tensorplay.arange(row, device=device).unsqueeze(1).repeat([1, col])
    cc = tensorplay.arange(col, device=device).unsqueeze(0).repeat([row, 1])
    mask = cc.le(rr + offset)
    r_flat = rr.masked_select(mask)
    c_flat = cc.masked_select(mask)
    out = tensorplay.stack([r_flat, c_flat], dim=0)
    if dtype != DType.int64:
        out = out.to(dtype)
    if pin_memory:
        out = out.pin_memory()
    return out


def triu_indices(row, col, offset=0, *, dtype=DType.int64, device=None,
                 pin_memory=False):
    row, col, offset = int(row), int(col), int(offset)
    if row < 0 or col < 0:
        raise ValueError("triu_indices(): row and col must be non-negative")
    rr = tensorplay.arange(row, device=device).unsqueeze(1).repeat([1, col])
    cc = tensorplay.arange(col, device=device).unsqueeze(0).repeat([row, 1])
    mask = cc.ge(rr + offset)
    r_flat = rr.masked_select(mask)
    c_flat = cc.masked_select(mask)
    out = tensorplay.stack([r_flat, c_flat], dim=0)
    if dtype != DType.int64:
        out = out.to(dtype)
    if pin_memory:
        out = out.pin_memory()
    return out


def cartesian_prod(*tensors):
    if len(tensors) == 0:
        raise RuntimeError("cartesian_prod(): expected at least one tensor")
    ts = [t if t.dim() >= 1 else t.reshape([1]) for t in tensors]
    if any(t.dim() != 1 for t in ts):
        raise NotImplementedError(
            "cartesian_prod(): only 1-D inputs are supported"
        )
    grids = tensorplay.meshgrid(list(ts), indexing="ij")
    cols = [g.reshape([-1, 1]).to(ts[0].dtype) for g in grids]
    return tensorplay.cat(cols, dim=1)



def combinations(input, r=2, with_replacement=False):
    n = input.size(0)
    r = int(r)
    if r < 0:
        raise ValueError("combinations(): r must be non-negative")
    rr = r
    gen = (itertools.combinations_with_replacement(range(n), r)
           if with_replacement else itertools.combinations(range(n), r))
    idx_list = list(gen)
    tail = list(input.size())[1:]
    if rr == 0:
        return tensorplay.zeros([0] + tail, dtype=input.dtype,
                                device=input.device)
    if not idx_list:
        return tensorplay.zeros([0] + tail, dtype=input.dtype,
                                device=input.device)
    idx = tensorplay.tensor(idx_list, dtype=DType.int64, device=input.device)
    picked = input.index_select(0, idx.reshape([-1]))
    return picked.reshape([idx.size(0), rr] + tail)


# ---------------------------------------------------------------------------
# statistics / numerics
# ---------------------------------------------------------------------------

_INT_DTYPES = (DType.uint8, DType.int8, DType.int16, DType.int32,
               DType.int64)


def _float_out_dtype(dt):
    return DType.float64 if dt == DType.float64 else DType.float32


def trapezoid(y, x=None, *, dx=None, dim=-1):
    d = _norm_dim(dim, y.dim())
    n = y.size(d)
    if n == 0:
        if x is not None:
            xs = _as_tensor(x)
            if xs.dim() == 1 and xs.numel() != 0:
                raise RuntimeError(
                    "trapezoid(): there must be one x value for every "
                    "sample in y along dim"
                )
        return y.narrow(d, 0, 0).sum(d)
    if x is None:
        step = dx if dx is not None else 1.0
        avg = y.narrow(d, 0, n - 1).add(y.narrow(d, 1, n - 1)).mul(step / 2.0)
    else:
        xs = _as_tensor(x)
        if xs.dim() == 1:
            if xs.numel() != y.size(d):
                raise RuntimeError(
                    "trapezoid(): there must be one x value for every "
                    "sample in y along dim"
                )
            view = [1] * y.dim()
            view[d] = xs.numel()
            xs = xs.reshape(view)
        n = y.size(d)
        dxs = xs.narrow(d, 1, n - 1).sub(xs.narrow(d, 0, n - 1))
        avg = y.narrow(d, 0, n - 1).add(y.narrow(d, 1, n - 1)) \
            .mul(0.5).mul(dxs)
    return avg.sum(d)


def trapz(y, x=None, *, dx=None, dim=-1):
    return trapezoid(y, x, dx=dx, dim=dim)


def cumulative_trapezoid(y, x=None, *, dx=None, dim=-1):
    d = _norm_dim(dim, y.dim())
    n = y.size(d)
    if n == 0:
        if x is not None:
            xs = _as_tensor(x)
            if xs.dim() == 1 and xs.numel() != 0:
                raise RuntimeError(
                    "cumulative_trapezoid(): there must be one x value for "
                    "every sample in y along dim"
                )
        return y.narrow(d, 0, 0)
    if x is None:
        step = dx if dx is not None else 1.0
        avg = y.narrow(d, 0, n - 1).add(y.narrow(d, 1, n - 1)).mul(step / 2.0)
    else:
        xs = _as_tensor(x)
        if xs.dim() == 1:
            if xs.numel() != y.size(d):
                raise RuntimeError(
                    "cumulative_trapezoid(): there must be one x value for "
                    "every sample in y along dim"
                )
            view = [1] * y.dim()
            view[d] = xs.numel()
            xs = xs.reshape(view)
        n = y.size(d)
        dxs = xs.narrow(d, 1, n - 1).sub(xs.narrow(d, 0, n - 1))
        avg = y.narrow(d, 0, n - 1).add(y.narrow(d, 1, n - 1)) \
            .mul(0.5).mul(dxs)
    return avg.cumsum(d)



[docs]
def quantile(input, q, dim=None, keepdim=False, *, interpolation="linear"):
    """Compute quantiles through the dispatcher composite implementation.

    All five interpolation modes, float32/float64, NaN semantics, and output
    shapes are handled here.  A Python-number q is
"""
    # A Python number becomes a tensor of the input's type; anything else
    # (a tensor, or a value standing for one under a trace) goes to the
    # binding, which rejects what is not a tensor.
    qs = q
    if isinstance(q, (int, float)) and not isinstance(q, tensorplay.Tensor):
        qs = tensorplay.tensor(q, dtype=input.dtype, device=input.device)
    return tensorplay._C.quantile(input, qs, dim=dim, keepdim=keepdim,
                                  interpolation=interpolation)




[docs]
def nanquantile(input, q, dim=None, keepdim=False, *, interpolation="linear"):
    # A Python number becomes a tensor of the input's type; anything else
    # (a tensor, or a value standing for one under a trace) goes to the
    # binding, which rejects what is not a tensor.
    qs = q
    if isinstance(q, (int, float)) and not isinstance(q, tensorplay.Tensor):
        qs = tensorplay.tensor(q, dtype=input.dtype, device=input.device)
    return tensorplay._C.nanquantile(input, qs, dim=dim, keepdim=keepdim,
                                     interpolation=interpolation)



def histc(input, bins=100, min=0, max=0):
    bins = int(bins)
    if bins <= 0:
        raise ValueError("histc(): bins must be a positive integer")
    v = input.reshape([-1]).to(DType.float64)
    lo, hi = float(min), float(max)
    if lo == 0 and hi == 0:
        if v.numel() == 0:
            return tensorplay.zeros(
                [bins], dtype=input.dtype, device=input.device)
        lo = v.min().item()
        hi = v.max().item()
    if lo > hi:
        raise RuntimeError("histc(): upper bound must be larger than "
                           "lower bound")
    if lo == hi or v.numel() == 0:
        return tensorplay.zeros([bins], dtype=input.dtype, device=input.device)
    width = (hi - lo) / bins
    in_range = _band(v.ge(lo), v.le(hi))
    idx = ((v - lo) / width).floor().to(DType.int64) \
        .clamp(0, bins - 1).masked_select(in_range)
    counts = tensorplay.bincount(idx, minlength=bins).narrow(0, 0, bins)
    return counts.to(input.dtype)



[docs]
def histogram(input, bins=100, range=None, *, weight=None, density=False):
    """
    composite): hist/bin_edges in the input dtype, linspace edges, aminmax
    outer-edge inference with the numpy empty-range expansion, weight in the
    input dtype and density normalization."""
    if isinstance(bins, tensorplay.Tensor):
        return tensorplay._C.histogram(input, bins, weight=weight,
                                       density=density)
    return tensorplay._C.histogram(input, bins, range=range, weight=weight,
                                   density=density)



def _weighted_bincount(idx, weight, nbins, density, widths):
    if weight is not None:
        w = weight.reshape([-1]).to(DType.float64)
        acc = tensorplay.zeros(
            [nbins], dtype=DType.float64, device=idx.device)
        acc = acc.index_put([idx], w, accumulate=True)
    else:
        acc = tensorplay.bincount(idx, minlength=nbins).narrow(0, 0, nbins) \
            .to(DType.float64)
    if density:
        total = acc.sum().item()
        if total > 0:
            acc = acc.div(widths.mul(total))
    return acc


def isin(elements, test_elements, *, assume_unique=False, invert=False):
    el = elements.reshape([-1])
    te = test_elements.reshape([-1])
    if te.numel() == 0:
        out = tensorplay.zeros(
            el.numel(), dtype=DType.bool, device=elements.device)
    else:
        ts, _ = tensorplay.sort(te)
        pos = tensorplay.searchsorted(ts, el, right=True)
        cand = pos.sub(1).clamp(0, ts.numel() - 1)
        found = ts.index_select(0, cand).eq(el)
        out = _band(found, pos.ge(1))
    out = out.reshape(elements.size())
    if invert:
        return out.logical_not()
    return out


def unique_consecutive(input, return_inverse=False, return_counts=False,
                       dim=None):
    if dim is not None:
        dim = _norm_dim(dim, input.dim())
        moved = tensorplay.movedim(input, dim, 0)
        n = moved.size(0)
        if n == 0:
            output = tensorplay.empty(
                list(input.shape), dtype=input.dtype, device=input.device)
            empty_index = tensorplay.empty(
                [0], dtype=DType.int64, device=input.device)
            if return_inverse and return_counts:
                return output, empty_index, empty_index
            if return_inverse:
                return output, empty_index
            if return_counts:
                return output, empty_index
            return output

        flat = moved.reshape([n, -1])
        if n == 1:
            change = tensorplay.ones(
                [1], dtype=DType.bool, device=input.device)
        else:
            adjacent = flat.narrow(0, 1, n - 1).ne(
                flat.narrow(0, 0, n - 1))
            change = tensorplay.cat(
                [tensorplay.ones([1], dtype=DType.bool, device=input.device),
                 tensorplay.any(adjacent, dim=1)],
                dim=0,
            )
        kept = tensorplay.nonzero(change).reshape([-1])
        num_runs = kept.numel()
        output = flat.index_select(0, kept).reshape(
            [num_runs] + list(moved.shape[1:]))
        output = tensorplay.movedim(output, 0, dim)
        inverse = change.to(DType.int64).cumsum(0).sub(1)
        counts = tensorplay.zeros(
            [num_runs], dtype=DType.int64, device=input.device)
        counts = counts.scatter_add(
            0, inverse,
            tensorplay.ones([n], dtype=DType.int64, device=input.device))
        if return_inverse and return_counts:
            return output, inverse, counts
        if return_inverse:
            return output, inverse
        if return_counts:
            return output, counts
        return output
    v = input.reshape([-1])
    n = v.numel()
    if n == 0:
        outs = [tensorplay.zeros([0], dtype=input.dtype, device=input.device)]
        if return_inverse:
            outs.append(tensorplay.zeros([0], dtype=DType.int64,
                                         device=input.device))
        if return_counts:
            outs.append(tensorplay.zeros([0], dtype=DType.int64,
                                         device=input.device))
        return outs[0] if len(outs) == 1 else tuple(outs)
    ne = v.narrow(0, 1, n - 1).ne(v.narrow(0, 0, n - 1))
    change = tensorplay.cat(
        [tensorplay.ones([1], dtype=DType.bool, device=input.device), ne])
    gid = change.to(DType.int64).cumsum(0).sub(1)
    values = v.masked_select(change)
    ngid = int(gid.narrow(0, n - 1, 1).item()) + 1
    outs = [values]
    if return_inverse:
        outs.append(gid)
    if return_counts:
        counts = tensorplay.zeros(
            [ngid], dtype=DType.int64, device=input.device) \
            .scatter_add(
                0, gid,
                tensorplay.ones([n], dtype=DType.int64, device=input.device))
        outs.append(counts)
    return outs[0] if len(outs) == 1 else tuple(outs)


def repeat_interleave(input, repeats, dim=None, *, output_size=None):
    src = input
    if dim is None:
        src = input.reshape([1, -1])
        axis = 1
    else:
        axis = _norm_dim(dim, input.dim())
    # Scalar repeats keep the repetition count on the host, so the result
    # shape is known without reading device data; an expanded view plus one
    # materializing copy replaces the index-map construction.
    if isinstance(repeats, (int, bool)) and not isinstance(
            repeats, tensorplay.Tensor):
        rep_n = int(repeats)
        if rep_n < 0:
            raise ValueError(
                "repeat_interleave(): repeats must be non-negative")
        n = src.size(axis)
        total = n * rep_n
        if output_size is not None and int(output_size) != total:
            raise RuntimeError(
                "repeat_interleave(): output_size does not match the repeated "
                f"length ({int(output_size)} != {total})"
            )
        if total == 0:
            output_shape = list(src.shape)
            output_shape[axis] = 0
            picked = tensorplay.empty(
                output_shape, dtype=input.dtype, device=input.device)
            return picked.reshape([-1]) if dim is None else picked
        view_shape = list(src.shape)
        view_shape.insert(axis + 1, rep_n)
        out_shape = list(src.shape)
        out_shape[axis] = total
        picked = src.unsqueeze(axis + 1).expand(view_shape) \
            .reshape(out_shape)
        if dim is None:
            return picked.reshape([-1])
        return picked
    if isinstance(repeats, tensorplay.Tensor) or not isinstance(
            repeats, (int, float, bool)):
        rep = _as_tensor(repeats).to(DType.int64).to(device=input.device)
        rep = rep.reshape([-1])
        if rep.numel() == 1 and src.size(axis) != 1:
            rep = tensorplay.full([src.size(axis)], int(rep[0].item()),
                                  dtype=DType.int64, device=input.device)
    else:
        rep = tensorplay.full([src.size(axis)], int(repeats),
                              dtype=DType.int64, device=input.device)
    if rep.numel() != src.size(axis):
        raise RuntimeError(
            "repeat_interleave(): repeats must have the same length as the "
            "selected dimension"
        )
    if bool((rep < 0).any()):
        raise ValueError("repeat_interleave(): repeats must be non-negative")
    ends = rep.cumsum(0)
    total = 0 if rep.numel() == 0 else int(ends[-1].item())
    if output_size is not None and int(output_size) != total:
        raise RuntimeError(
            "repeat_interleave(): output_size does not match the repeated "
            f"length ({int(output_size)} != {total})"
        )
    if total == 0:
        output_shape = list(src.shape)
        output_shape[axis] = 0
        picked = tensorplay.empty(
            output_shape, dtype=input.dtype, device=input.device)
        return picked.reshape([-1]) if dim is None else picked
    pos = tensorplay.arange(total, dtype=DType.int64, device=input.device)
    seg = tensorplay.searchsorted(ends.contiguous(), pos, right=True) \
        .clamp(0, src.size(axis) - 1)
    moved = src.transpose(0, axis) if axis != 0 else src
    picked = moved.index_select(0, seg)
    if axis != 0:
        picked = picked.transpose(0, axis)
    if dim is None:
        return picked.reshape([-1])
    return picked


def kaiser_window(window_length, periodic=True, beta=12.0, *,
                  dtype=None, layout=None, device=None, pin_memory=False,
                  requires_grad=False):
    n = int(window_length)
    if n < 0:
        raise ValueError("kaiser_window(): window_length must be non-negative")
    if layout not in (None, tensorplay.strided):
        raise ValueError("kaiser_window(): only strided layout is supported")
    if dtype is None:
        dtype = tensorplay.get_default_dtype()
    if n <= 1:
        w = tensorplay.ones([n], dtype=dtype, device=device)
        if pin_memory:
            w = w.pin_memory()
        return w.requires_grad_(True) if requires_grad else w
    denom = n if periodic else n - 1
    if denom > 0:
        t = tensorplay.arange(n, dtype=DType.float64, device=device)
        alpha = denom / 2.0
        x = (t - alpha) / alpha
        arg = (1.0 - x.pow(2)).clamp(min=0.0).sqrt().mul(float(beta))

        def _i0_series(z):
            # I0(z) = sum_k ((z^2)/4)^k / (k!)^2, converged in float64
            out = tensorplay.ones_like(z)
            term = tensorplay.ones_like(z)
            for k in range(1, 40):
                term = term.mul(z.mul(z).div(4.0)).div(k * k)
                out = out.add(term)
            return out

        w = _i0_series(arg).div(_i0_series(
            tensorplay.full([], float(beta), dtype=DType.float64,
                            device=device)))
    else:
        w = tensorplay.ones([n], dtype=DType.float64, device=device)
    w = w.to(dtype)
    if pin_memory:
        w = w.pin_memory()
    return w.requires_grad_(True) if requires_grad else w


# ---------------------------------------------------------------------------
# rnn cells
# ---------------------------------------------------------------------------

def lstm_cell(input, hx, cx, w_ih, w_hh, b_ih=None, b_hh=None):
    gates = input.mm(w_ih.t()).add(hx.mm(w_hh.t()))
    if b_ih is not None:
        gates = gates.add(b_ih)
    if b_hh is not None:
        gates = gates.add(b_hh)
    w = gates.size(1) // 4
    gi = gates.narrow(1, 0, w)
    gf = gates.narrow(1, w, w)
    go = gates.narrow(1, 2 * w, w)
    gg = gates.narrow(1, 3 * w, w)
    i = gi.sigmoid()
    f = gf.sigmoid()
    g = gg.tanh()
    o = go.sigmoid()
    cy = f.mul(cx).add(i.mul(g))
    hy = o.mul(cy.tanh())
    return hy, cy


def rnn_relu_cell(input, hx, w_ih, w_hh, b_ih=None, b_hh=None):
    h = input.mm(w_ih.t()).add(hx.mm(w_hh.t()))
    if b_ih is not None:
        h = h.add(b_ih)
    if b_hh is not None:
        h = h.add(b_hh)
    return h.relu()


def rnn_tanh_cell(input, hx, w_ih, w_hh, b_ih=None, b_hh=None):
    h = input.mm(w_ih.t()).add(hx.mm(w_hh.t()))
    if b_ih is not None:
        h = h.add(b_ih)
    if b_hh is not None:
        h = h.add(b_hh)
    return h.tanh()


# ---------------------------------------------------------------------------
# misc tensor utilities
# ---------------------------------------------------------------------------

def put(input, index, source):
    flat = input.clone().reshape([-1])
    idx = index.to(DType.int64).reshape([-1])
    src = source.to(input.dtype).reshape([-1])
    if src.numel() == 0 and idx.numel() > 0:
        raise RuntimeError("put(): source must not be empty when indices "
                           "are given")
    if src.numel() < idx.numel():
        reps = (idx.numel() + src.numel() - 1) // src.numel()
        src = src.repeat([reps])
    if src.numel() != idx.numel():
        src = src.narrow(0, 0, idx.numel())
    flat = flat.index_put([idx], src)
    return flat.reshape(list(input.size()))


def resolve_conj(input):
    return input


def resolve_neg(input):
    return input


def is_conj(input):
    return False


def is_neg(input):
    return False


_PROMOTE_TABLE = {
    "bool": {'bool': 'bool', 'uint8': 'uint8', 'int8': 'int8', 'int16': 'int16', 'int32': 'int32', 'int64': 'int64', 'float16': 'float16', 'bfloat16': 'bfloat16', 'float32': 'float32', 'float64': 'float64', 'complex64': 'complex64', 'complex128': 'complex128'},
    "uint8": {'bool': 'uint8', 'uint8': 'uint8', 'int8': 'int16', 'int16': 'int16', 'int32': 'int32', 'int64': 'int64', 'float16': 'float16', 'bfloat16': 'bfloat16', 'float32': 'float32', 'float64': 'float64', 'complex64': 'complex64', 'complex128': 'complex128'},
    "int8": {'bool': 'int8', 'uint8': 'int16', 'int8': 'int8', 'int16': 'int16', 'int32': 'int32', 'int64': 'int64', 'float16': 'float16', 'bfloat16': 'bfloat16', 'float32': 'float32', 'float64': 'float64', 'complex64': 'complex64', 'complex128': 'complex128'},
    "int16": {'bool': 'int16', 'uint8': 'int16', 'int8': 'int16', 'int16': 'int16', 'int32': 'int32', 'int64': 'int64', 'float16': 'float16', 'bfloat16': 'bfloat16', 'float32': 'float32', 'float64': 'float64', 'complex64': 'complex64', 'complex128': 'complex128'},
    "int32": {'bool': 'int32', 'uint8': 'int32', 'int8': 'int32', 'int16': 'int32', 'int32': 'int32', 'int64': 'int64', 'float16': 'float16', 'bfloat16': 'bfloat16', 'float32': 'float32', 'float64': 'float64', 'complex64': 'complex64', 'complex128': 'complex128'},
    "int64": {'bool': 'int64', 'uint8': 'int64', 'int8': 'int64', 'int16': 'int64', 'int32': 'int64', 'int64': 'int64', 'float16': 'float16', 'bfloat16': 'bfloat16', 'float32': 'float32', 'float64': 'float64', 'complex64': 'complex64', 'complex128': 'complex128'},
    "float16": {'bool': 'float16', 'uint8': 'float16', 'int8': 'float16', 'int16': 'float16', 'int32': 'float16', 'int64': 'float16', 'float16': 'float16', 'bfloat16': 'float32', 'float32': 'float32', 'float64': 'float64', 'complex64': 'complex64', 'complex128': 'complex128'},
    "bfloat16": {'bool': 'bfloat16', 'uint8': 'bfloat16', 'int8': 'bfloat16', 'int16': 'bfloat16', 'int32': 'bfloat16', 'int64': 'bfloat16', 'float16': 'float32', 'bfloat16': 'bfloat16', 'float32': 'float32', 'float64': 'float64', 'complex64': 'complex64', 'complex128': 'complex128'},
    "float32": {'bool': 'float32', 'uint8': 'float32', 'int8': 'float32', 'int16': 'float32', 'int32': 'float32', 'int64': 'float32', 'float16': 'float32', 'bfloat16': 'float32', 'float32': 'float32', 'float64': 'float64', 'complex64': 'complex64', 'complex128': 'complex128'},
    "float64": {'bool': 'float64', 'uint8': 'float64', 'int8': 'float64', 'int16': 'float64', 'int32': 'float64', 'int64': 'float64', 'float16': 'float64', 'bfloat16': 'float64', 'float32': 'float64', 'float64': 'float64', 'complex64': 'complex128', 'complex128': 'complex128'},
    "complex64": {'bool': 'complex64', 'uint8': 'complex64', 'int8': 'complex64', 'int16': 'complex64', 'int32': 'complex64', 'int64': 'complex64', 'float16': 'complex64', 'bfloat16': 'complex64', 'float32': 'complex64', 'float64': 'complex128', 'complex64': 'complex64', 'complex128': 'complex128'},
    "complex128": {'bool': 'complex128', 'uint8': 'complex128', 'int8': 'complex128', 'int16': 'complex128', 'int32': 'complex128', 'int64': 'complex128', 'float16': 'complex128', 'bfloat16': 'complex128', 'float32': 'complex128', 'float64': 'complex128', 'complex64': 'complex128', 'complex128': 'complex128'},
}


_RANK = {
    DType.bool: 0,
    DType.uint8: 1, DType.int8: 2, DType.int16: 3, DType.int32: 4,
    DType.int64: 5,
    DType.float16: 6, DType.bfloat16: 6, DType.float32: 7, DType.float64: 8,
    DType.complex64: 9, DType.complex128: 10,
}
_CATEGORY = {
    DType.bool: 0,
    DType.uint8: 1, DType.int8: 1, DType.int16: 1, DType.int32: 1,
    DType.int64: 1,
    DType.float16: 2, DType.bfloat16: 2, DType.float32: 2, DType.float64: 2,
    DType.complex64: 3, DType.complex128: 3,
}


def _dt(dt):
    return dt if isinstance(dt, DType) else DType(dt)


def promote_types(type1, type2):
    a, b = _dt(type1), _dt(type2)
    an, bn = a.name, b.name
    if an in _PROMOTE_TABLE and bn in _PROMOTE_TABLE:
        return getattr(DType, _PROMOTE_TABLE[an][bn])
    if _CATEGORY.get(a) != _CATEGORY.get(b):
        hi = a if _CATEGORY.get(a, 0) > _CATEGORY.get(b, 0) else b
        cat = _CATEGORY[hi]
        for name in ("float32", "complex64"):
            if getattr(DType, name) is not None and \
                    _CATEGORY.get(getattr(DType, name)) == cat:
                pass
        return hi
    return a if _RANK[a] >= _RANK[b] else b


def can_cast(from_, to):
    a, b = _dt(from_), _dt(to)
    an, bn = a.name, b.name
    if an in _PROMOTE_TABLE and bn in _PROMOTE_TABLE:
        return _PROMOTE_TABLE[an][bn] == bn
    ca, cb = _CATEGORY[a], _CATEGORY[b]
    if ca != cb:
        return ca < cb
    return _RANK[a] <= _RANK[b]


def result_type(*args):
    """The type an elementwise operation on these operands produces.

    Operands rank by category: tensors with dimensions over 0-dim tensors over
    plain numbers, and within a category by promotion.  A number takes part
    as a wrapped number -- its kind counts, never a width: a whole number
    beside a 32-bit tensor stays 32-bit, a real number turns a whole tensor
    into the default real type, and a complex number keeps a real tensor's
    width in the complex result.
    """

    dim_result = zero_result = wrapped_result = None

    def promote(a, b):
        return b if a is None else (a if b is None else promote_types(a, b))

    for item in args:
        if isinstance(item, tensorplay.Tensor):
            if item.dim() > 0:
                dim_result = promote(dim_result, item.dtype)
            else:
                zero_result = promote(zero_result, item.dtype)
            continue
        if isinstance(item, bool):
            dt = DType.bool
        elif isinstance(item, int):
            dt = DType.int64
        elif isinstance(item, float):
            dt = tensorplay.get_default_dtype()
        elif isinstance(item, complex):
            dt = DType.complex128 if tensorplay.get_default_dtype() == DType.float64 else DType.complex64
        else:
            dt = _dt(item)
        wrapped_result = promote(wrapped_result, dt)

    def combine(higher, lower):
        if higher is not None and higher.is_complex:
            return higher
        if lower is not None and lower.is_complex:
            if higher is not None and higher.is_floating_point:
                # The real tensor's width carries into the complex result.
                return {
                    DType.float16: DType.complex32,
                    DType.float32: DType.complex64,
                    DType.float64: DType.complex128,
                }.get(higher, lower)
            return lower
        if higher is not None and higher.is_floating_point:
            return higher
        if higher == DType.bool or (lower is not None and lower.is_floating_point):
            return promote(higher, lower)
        return higher if higher is not None else lower

    return combine(dim_result, combine(zero_result, wrapped_result))


def is_nonzero(input):
    n = input.numel()
    if n == 0:
        raise RuntimeError(
            "Boolean value of Tensor with no values is ambiguous"
        )
    if n != 1:
        raise RuntimeError(
            "Boolean value of Tensor with more than one value is ambiguous"
        )
    return input.item() != 0


def is_same_size(input, other):
    return list(input.size()) == list(other.size())


def get_device(input):
    idx = getattr(input.device, "index", None)
    return -1 if idx is None else int(idx)


# ---------------------------------------------------------------------------
# *_copy family
# ---------------------------------------------------------------------------

def alias_copy(input):
    return input.clone()


def t_copy(input):
    return input.t().clone()


def permute_copy(input, dims):
    return input.permute(list(dims)).clone()


def transpose_copy(input, dim0, dim1):
    return input.transpose(dim0, dim1).clone()


def squeeze_copy(input, dim=None):
    if dim is None:
        return input.squeeze()
    return input.squeeze(dim)


def unsqueeze_copy(input, dim):
    return input.unsqueeze(dim)


def select_copy(input, dim, index):
    return input.select(dim, index).clone()


def slice_copy(input, dim=0, start=None, end=None, step=1):
    d = _norm_dim(dim, input.dim())
    n = input.size(d)
    start = 0 if start is None else int(start)
    end = n if end is None else int(end)
    return _slice_getitem(input, d, start, end, step).contiguous()


def narrow_copy(input, dim, start, length):
    return input.narrow(dim, start, length).clone()


def diagonal_copy(input, diagonal=0, dim1=-2, dim2=-1):
    return input.diagonal(diagonal, dim1, dim2).clone()


def unbind_copy(input, dim=0):
    return [t.clone() for t in tensorplay.unbind(input, dim)]


def split_copy(input, split_size_or_sections, dim=0):
    parts = tensorplay.split(input, split_size_or_sections, dim)
    return [p.clone() for p in parts]


def view_copy(input, size):
    return input.view(list(size)).clone()


def reshape_as(input, other):
    # input.reshape(other.shape).
    return input.reshape(list(other.shape))


def unfold_copy(input, dimension, size, step):
    return input.unfold(dimension, size, step).clone()


def expand_copy(input, size, *, implicit=False):
    return input.expand(list(size)).clone()


def unsafe_chunk(input, chunks, dim=0):
    return tensorplay.chunk(input, chunks, dim)


def unsafe_split(input, split_size, dim=0):
    return tensorplay.split(input, split_size, dim)


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

def rms_norm(input, normalized_shape, weight=None, eps=None):
    from tensorplay.nn import functional as F
    return F.rms_norm(input, list(normalized_shape), weight, eps)


def cosine_similarity(x1, x2, dim=1, eps=1e-8):
    from tensorplay.nn import functional as F
    return F.cosine_similarity(x1, x2, dim, eps)


def max_pool1d(input, kernel_size, stride=None, padding=0, dilation=1,
               ceil_mode=False):
    from tensorplay.nn import functional as F
    return F.max_pool1d(input, kernel_size, stride=stride, padding=padding,
                        dilation=dilation, ceil_mode=ceil_mode)


def avg_pool1d(input, kernel_size, stride=None, padding=0, ceil_mode=False,
               count_include_pad=True):
    from tensorplay.nn import functional as F
    return F.avg_pool1d(input, kernel_size, stride=stride, padding=padding,
                        ceil_mode=ceil_mode,
                        count_include_pad=count_include_pad)


def adaptive_avg_pool1d(input, output_size):
    from tensorplay.nn import functional as F
    return F.adaptive_avg_pool1d(input, output_size)


def adaptive_max_pool1d(input, output_size):
    from tensorplay.nn import functional as F
    return F.adaptive_max_pool1d(input, output_size)


# ---------------------------------------------------------------------------
# exposes at top level. Lazy import: nn.functional is not available during
# the tensorplay bootstrap (this module loads before the nn package).
# ---------------------------------------------------------------------------


def dropout(input, p=0.5, training=True, inplace=False):
    from tensorplay.nn import functional as _F
    return _F.dropout(input, p=p, training=training, inplace=inplace)


def dropout_(input, p=0.5, training=True):
    from tensorplay.nn import functional as _F
    return _F.dropout_(input, p=p, training=training)


def alpha_dropout(input, p=0.5, training=True, inplace=False):
    from tensorplay.nn import functional as _F
    return _F.alpha_dropout(input, p=p, training=training, inplace=inplace)


def feature_dropout(input, p=0.5, training=False, inplace=False):
    from tensorplay.nn import functional as _F
    return _F.feature_dropout(input, p=p, training=training,
                              inplace=inplace)


def feature_dropout_(input, p=0.5, training=True):
    from tensorplay.nn import functional as _F
    return _F.feature_dropout_(input, p=p, training=training)


def feature_alpha_dropout(input, p=0.5, training=False, inplace=False):
    from tensorplay.nn import functional as _F
    return _F.feature_alpha_dropout(input, p=p, training=training,
                                    inplace=inplace)


def rrelu(input, lower=1.0 / 8, upper=1.0 / 3, training=False,
          inplace=False):
    from tensorplay.nn import functional as _F
    return _F.rrelu(input, lower=lower, upper=upper, training=training,
                    inplace=inplace)


def rrelu_(input, lower=1.0 / 8, upper=1.0 / 3, training=False):
    from tensorplay.nn import functional as _F
    return _F.rrelu_(input, lower=lower, upper=upper, training=training)


def bilinear(input1, input2, weight, bias=None):
    from tensorplay.nn import functional as _F
    return _F.bilinear(input1, input2, weight, bias)


def ctc_loss(log_probs, targets, input_lengths, target_lengths,
             blank=0, reduction="mean", zero_infinity=False):
    from tensorplay.nn import functional as _F
    return _F.ctc_loss(log_probs, targets, input_lengths, target_lengths,
                       blank=blank, reduction=reduction,
                       zero_infinity=zero_infinity)


def embedding_bag(input, weight, offsets=None, max_norm=None,
                  norm_type=2, scale_grad_by_freq=False, mode="mean",
                  sparse=False, per_sample_weights=None,
                  include_last_offset=False, padding_idx=None):
    from tensorplay.nn import functional as _F
    return _F.embedding_bag(input, weight, offsets=offsets,
                            max_norm=max_norm, norm_type=norm_type,
                            scale_grad_by_freq=scale_grad_by_freq,
                            mode=mode, sparse=sparse,
                            per_sample_weights=per_sample_weights,
                            include_last_offset=include_last_offset,
                            padding_idx=padding_idx)


def conv_tbc(input, weight, bias=None, pad=0):
    from tensorplay.nn import functional as _F
    return _F.conv_tbc(input, weight, bias, pad=pad)


def max_pool3d(input, kernel_size, stride=None, padding=0, dilation=1,
               ceil_mode=False, return_indices=False):
    from tensorplay.nn import functional as _F
    return _F.max_pool3d(input, kernel_size, stride=stride, padding=padding,
                         dilation=dilation, ceil_mode=ceil_mode,
                         return_indices=return_indices)


def max_pool1d_with_indices(input, kernel_size, stride=None, padding=0,
                            dilation=1, ceil_mode=False):
    from tensorplay.nn import functional as _F
    return _F.max_pool1d_with_indices(input, kernel_size, stride=stride,
                                      padding=padding, dilation=dilation,
                                      ceil_mode=ceil_mode)


def native_channel_shuffle(input, groups):
    from tensorplay.nn import functional as _F
    return _F.channel_shuffle(input, groups)
Ask DeepWiki