Description
This issue seeks to come to a consensus on a subset of type promotion rules (i.e., the rules governing the common result type for two array operands during an arithmetic operation) suitable for specification.
As initially discussed in #13, a universal set of type promotion rules can be difficult to standardize due to the needs/constraints of particular runtime environments. However, we should be able to specify a minimal set of type promotion rules which all specification conforming array libraries can, and should, support.
Prior Art
- NumPy: promotion rules follow a type hierarchy (where complex > floating > integral > boolean). See
promote_types
andresult_type
APIs and source [1, 2]. - CuPy: follows NumPy's rules, except for zero-dimension arrays.
- Dask: follows NumPy's rules.
- JAX: type promotion table (and source) which deviates from NumPy's promotion rules in two ways: (1) biased toward half- and single-precision floating-point numbers and (2) support for a non-standard floating-point type.
- PyTorch: promotion rules follow a type hierarchy (where complex > floating > integral > boolean) without inspection of value magnitude.
- Tensorflow: requires explicit casting.
Proposal
This issue proposes to specify that all specification conforming array libraries must, at minimum, support the following type promotions:
-
floating-point type promotion table:
f2 f4 f8 f2 f2 f4 f8 f4 f4 f4 f8 f8 f8 f8 f8 where
- f2: half-precision (16-bit) floating-point number
- f4: single-precision (32-bit) floating-point number
- f8: double-precision (64-bit) floating-point number
-
unsigned integer type promotion table:
u1 u2 u4 u8 u1 u1 u2 u4 u8 u2 u2 u2 u4 u8 u4 u4 u4 u4 u8 u8 u8 u8 u8 u8 where
- u1: 8-bit unsigned integer
- u2: 16-bit unsigned integer
- u4: 32-bit unsigned integer
- u8: 64-bit unsigned integer
-
signed integer type promotion table:
i1 i2 i4 i8 i1 i1 i2 i4 i8 i2 i2 i2 i4 i8 i4 i4 i4 i4 i8 i8 i8 i8 i8 i8 where
- i1: 8-bit signed integer
- i2: 16-bit signed integer
- i4: 32-bit signed integer
- i8: 64-bit signed integer
-
mixed unsigned and signed integer type promotion table:
u1 u2 u4 i1 i2 i4 i8 i2 i2 i4 i8 i4 i4 i4 i8
Notes
- The minimal set of type promotions outlined above explicitly does not define promotions between types which are not of the same kind (i.e., floating-point versus integer). When converting between types of different kinds, libraries tend to support C type promotion semantics, where floating-point, regardless of precision, has a higher rank/precedence than all integer types; however, they differ in the promoted floating-point precision (e.g., JAX promotes
(i8, f2)
tof2
, while NumPy promotes(i8, f2)
tof8
). The reason for the discrepancy stems from the particular needs/constraints of accelerator devices, and, thus, by omitting specification here, we allow for implementation flexibility and avoid imposing undue burden. - Omitted from the above tables are "unsafe" promotions. Notably, not included are promotion rules for mixed signed/unsigned 64-bit integers
i8
andu8
. NumPy and JAX both promote(i8, u8)
tof8
which is explicitly undefined via the aforementioned note regarding conversions between kinds and also raises questions regarding inexact rounding when converting from a 64-bit integer to double-precision floating-point. - This proposal addresses type promotion among array operands, including zero-dimensional arrays. It remains to be decided whether "scalars" (i.e., non-array operands) should directly participate in type promotion.