Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 92 additions & 8 deletions onnxscript/function_libs/torch_lib/ops/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from __future__ import annotations

import functools
import math
from typing import Any, Optional, Sequence, Tuple, Union

Expand Down Expand Up @@ -58,6 +59,17 @@
_MATH_PI = math.pi


@functools.lru_cache(maxsize=None)
def _int64_arange_non_integral_uses_float_length() -> bool:
"""Whether torch computes the arange length in float when dtype is integral.

Newer versions of torch return 4 elements for ``torch.arange(3.1, dtype=torch.int64)``,
while older versions return 3. This is evaluated lazily to avoid running torch code
at import time.
"""
return torch.arange(3.1, dtype=torch.int64, device="cpu").numel() == 4


@torch_op("aten::_local_scalar_dense", trace_only=True)
def aten__local_scalar_dense(self: TensorType) -> TensorType:
"""_local_scalar_dense(Tensor self) -> Scalar"""
Expand Down Expand Up @@ -521,6 +533,56 @@ def _range_supported(dtype: int) -> bool:
}


def _is_integral_dtype(dtype: int) -> bool:
return dtype in {
INT8.dtype,
INT16.dtype,
INT32.dtype,
INT64.dtype,
}


def _is_integral_scalar(arg: TRealUnlessFloat16OrInt8) -> bool:
if isinstance(arg, int):
return True
if isinstance(arg, float):
return False
return arg.dtype in {
INT8.dtype,
INT16.dtype,
INT32.dtype,
INT64.dtype,
}


def _arange_integral_dtype_with_non_integral_args(
start: TRealUnlessFloat16OrInt8,
end: TRealUnlessFloat16OrInt8,
step: TRealUnlessFloat16OrInt8,
dtype: int,
) -> TensorType:
"""Implements torch.arange for integral dtypes when not all inputs are integral."""
start_float = op.Cast(start, to=FLOAT.dtype)
end_float = op.Cast(end, to=FLOAT.dtype)
step_float = op.Cast(step, to=FLOAT.dtype)

length = op.Cast(
op.Ceil(op.Div(op.Sub(end_float, start_float), step_float)), to=INT64.dtype
)
index = op.Range(op.Constant(value_int=0), length, op.Constant(value_int=1))

if _range_supported(dtype):
index = op.Cast(index, to=dtype)
start = op.Cast(start, to=dtype)
step = op.Cast(step, to=dtype)
return op.Add(start, op.Mul(step, index))

start = op.Cast(start, to=INT64.dtype)
step = op.Cast(step, to=INT64.dtype)
result = op.Add(start, op.Mul(step, index))
return op.Cast(result, to=dtype)


def _integral_to_be_adjusted(dtype: int) -> bool:
"""Returns true if the dtype is special integral handled by torch."""
return dtype in {
Expand All @@ -544,6 +606,12 @@ def aten_arange(
zero = op.CastLike(0.0, end)
one = op.CastLike(1.0, end)
result = op.Range(zero, end, one)
elif (
_is_integral_dtype(dtype)
and not _is_integral_scalar(end)
and (dtype != INT64.dtype or _int64_arange_non_integral_uses_float_length())
):
result = _arange_integral_dtype_with_non_integral_args(0, end, 1, dtype)
elif _range_supported(dtype):
end = op.Cast(end, to=dtype)
zero = op.Cast(0, to=dtype)
Expand Down Expand Up @@ -576,6 +644,12 @@ def aten_arange_start(
if dtype == -1 or dtype is None:
one = op.CastLike(1.0, end)
result = op.Range(start, end, one)
elif (
_is_integral_dtype(dtype)
and not (_is_integral_scalar(start) and _is_integral_scalar(end))
and (dtype != INT64.dtype or _int64_arange_non_integral_uses_float_length())
):
result = _arange_integral_dtype_with_non_integral_args(start, end, 1, dtype)
elif _range_supported(dtype):
end = op.Cast(end, to=dtype)
start = op.Cast(start, to=dtype)
Expand Down Expand Up @@ -659,17 +733,26 @@ def aten_arange_start_step(
end = op.Cast(end, to=FLOAT.dtype)
step = op.Cast(step, to=FLOAT.dtype)
result = op.Range(start, end, step)
elif _integral_to_be_adjusted(dtype):
# PyTorch arange op handles these integral types differently from INT64,
# so we have to adjust these arguments accordingly.
# https://github.com/pytorch/pytorch/blob/121cfb60c0817816fcbe2190303b7f6d05c77cf3/torch/_refs/__init__.py#L4794
start, end, step = _adjust_args_for_arange_int_dtype(start, end, step)
result = op.Cast(op.Range(start, end, step), to=dtype)
elif (
_is_integral_dtype(dtype)
and not (
_is_integral_scalar(start)
and _is_integral_scalar(end)
and _is_integral_scalar(step)
)
and (dtype != INT64.dtype or _int64_arange_non_integral_uses_float_length())
):
result = _arange_integral_dtype_with_non_integral_args(start, end, step, dtype)
elif dtype == INT64.dtype:
end = op.Cast(end, to=dtype)
start = op.Cast(start, to=dtype)
step = op.Cast(step, to=dtype)
result = op.Range(start, end, step)
elif _integral_to_be_adjusted(dtype):
# PyTorch arange op handles these integral types differently from INT64
# when all arguments are integral.
start, end, step = _adjust_args_for_arange_int_dtype(start, end, step)
result = op.Cast(op.Range(start, end, step), to=dtype)
else:
# Cast input to float if dtype is not supported by Range,
# because the input dtype may be e.g. bfloat16,
Expand Down Expand Up @@ -5852,8 +5935,9 @@ def aten_logit(self: TFloat, eps: Optional[float] = None) -> TFloat:
one_minus_eps = ir.tensor(1 - eps, dtype=self.dtype)
eps = ir.tensor(eps, dtype=self.dtype)

temporary_self = op.Where(self <= one_minus_eps, self, one_minus_eps)
z = op.Where(temporary_self < eps, eps, temporary_self)
# Match torch.clamp behavior for eps > 0.5 by applying max then min.
z = op.Where(self < eps, eps, self)
z = op.Where(z <= one_minus_eps, z, one_minus_eps)

return op.Log(op.Div(z, op.Sub(one, z)))

Expand Down
Loading