|
Uni20
Asynchronous tensor and runtime library.
|
Status: design note for future Python Tensor arithmetic. The current Python extension does not yet expose Tensor values.
This document proposes a promotion contract. The contract becomes authoritative only when Tensor arithmetic is implemented and covered by binding tests. Operation wrappers should then share one policy rather than defining independent promotion rules.
The initial numerical contract covers enabled real floating and complex floating Tensor dtypes. Integer and boolean tensors may be added for indices, masks, charges, and selected arithmetic, but mixed numerical behavior is not implicitly enabled until it has its own table and tests.
The set of exposed dtypes is generated from one binding scalar list. Optional extended precision may participate in Uni20-to-Uni20 operations when configured, but NumPy and DLPack interoperability is exposed only where those protocols have a compatible dtype contract.
These rules follow NumPy 2.x and the Python Array API where that behavior is appropriate, without inheriting unspecified or accidental corner cases.
For the initial 32-bit and 64-bit floating families:
| lhs | rhs | result |
|---|---|---|
float32 | float32 | float32 |
float32 | float64 | float64 |
float32 | complex64 | complex64 |
float32 | complex128 | complex128 |
float64 | float64 | float64 |
float64 | complex64 | complex128 |
float64 | complex128 | complex128 |
complex64 | complex64 | complex64 |
complex64 | complex128 | complex128 |
complex128 | complex128 | complex128 |
The table is symmetric. An enabled real or complex extended-precision dtype extends the same precision lattice, subject to operation and backend support.
Result dtype is computed before kernel selection. A provider decline may select another backend, but it does not silently narrow or widen the result.
For Tensor and ordinary Python scalar arithmetic:
| Tensor dtype | Python scalar kind | result |
|---|---|---|
float32 | int or float | float32 |
float32 | complex | complex64 |
float64 | int or float | float64 |
float64 | complex | complex128 |
complex64 | int, float, or complex | complex64 |
complex128 | int, float, or complex | complex128 |
The scalar value must still be representable according to the operation's conversion policy. Preserving float32 does not require silently accepting a Python integer too large to convert meaningfully.
NumPy scalar objects and zero-dimensional arrays are array-like typed values, not untyped Python scalars. Their explicit dtype participates in the tensor-tensor promotion rule.
In-place arithmetic preserves the existing Tensor dtype and allocation:
The right operand may be converted to the existing dtype only when the operation permits it. In-place arithmetic never replaces storage merely to accommodate a promoted result.
True division has an operation-specific result rule:
Reduction result and accumulator dtype are separate questions. Each reduction must specify:
No default accumulator widening is inferred from an incidental C++ host scalar.
An algorithm may use a wider internal scalar for error estimation, projected subspaces, or accumulation. That is distinct from promoting the full Tensor operation.
For example, diagnostics for a Krylov operation may report:
Such widening is an explicit algorithm policy. It does not arise because a normalization scalar happened to be represented as C++ double.
Python scalar conversion is centralized. Individual bindings do not separately interpret int, float, complex, NumPy scalars, or zero-dimensional arrays.
Typed native operations remain free to use distinct scalar types for output and inputs:
The binding does not cast both inputs to the output dtype merely to fit a single-dtype implementation. An operation materializes converted inputs only when its documented algorithm requires that conversion.
Tests cover every exposed pair of Tensor dtypes, operand order, Python scalar kind, in-place rejection, backend fallback, and configured extended-precision case.
Numerical diagnostics should make visible:
float16 and bfloat16 families.These extensions should add rows to the shared contract rather than creating operation-local promotion rules.