Mathematical Expressions#
DALI allows you to use regular Python arithmetic operations
and other mathematical functions in
the pipeline definition (via @pipeline_def
or within
define_graph()
) on the values that are returned from invoking
other operators.
The expressions that are used will be incorporated into the pipeline without needing to explicitly instantiate operators and will describe the element-wise operations on tensors:
@pipeline_def
def my_pipeline():
"""Create a pipeline which reads and decodes the images, scales channels by
broadcasting and clamps the result to [128, 255) range."""
img_files, _ = fn.readers.file(file_root="image_dir")
images = fn.decoders.image(img_files, device="mixed")
red_highlight = images * nvidia.dali.types.Constant(np.float32([1.25, 0.75, 0.75]))
result = nvidia.dali.math.clamp(red_highlight, 128, 255)
return result
At least one of the inputs to the arithmetic expression must be returned by other DALI operator -
that is a value of nvidia.dali.pipeline.DataNode
representing a batch of tensors.
The other input can be nvidia.dali.types.Constant()
or regular Python value of type bool
,
int
, or float
. As the operations performed are element-wise, the shapes of all
operands must be compatible - either match exactly or be broadcastable.
For details and examples see expressions tutorials.
Note
Keep in mind to wrap the tensor constants used in mathematical expressions (like NumPy array)
with the nvidia.dali.types.Constant()
. If used directly, the operator implementation
from that tensor’s library may be picked up and the result might be undefined. Using the line
from the previous example, the first two variants are equivalent, while the third will be wrong:
# Correct approach:
red_highlight_0 = images *
nvidia.dali.types.Constant(np.float32([1.25, 0.75, 0.75]))
red_highlight_1 = nvidia.dali.types.Constant(np.float32([1.25, 0.75, 0.75])) *
images
# Wrong approach:
# red_highlight_2 = np.float32([1.25, 0.75, 0.75]) * images
Type Promotion Rules#
For operations that accept two (or more) arguments, type promotions apply. The resulting type is calculated in accordance to the table below.
Operand Type
Operand Type
Result Type
Additional Conditions
T
T
T
floatX
T
floatX
where T is not a float
floatX
floatY
floatZ
where Z = max(X, Y)
intX
intY
intZ
where Z = max(X, Y)
uintX
uintY
uintZ
where Z = max(X, Y)
intX
uintY
int2Y
if X <= Y
intX
uintY
intX
if X > Y
T
stands for any one of the supported numerical types:
bool
, int8
, int16
, int32
, int64
, uint8
, uint16
,
uint32
, uint64
, float32
, and float64
.
bool
type is considered the smallest unsigned integer type and is treated as uint1
with respect to the table above.
Note
Type promotion is commutative.
For more than two arguments, the resulting type is calculated as a reduction from left to right - first calculating the result of operating on first two arguments, next between that intermediate result and the third argument and so on, until we have only the result type left.
Supported Arithmetic Operations#
Currently, DALI supports the following operations:
- Unary arithmetic operators: +, -
Unary operators that implement
__pos__(self)
and__neg__(self)
. The result of a unary arithmetic operation always preserves the input type. Unary operators accept only TensorList inputs from other operators.- Return type:
TensorList of the same type
- Binary arithmetic operations: +, -, *, /, //, **
Binary operators that implement
__add__
,__sub__
,__mul__
,__truediv__
,__floordiv__
and__pow__
respectively.The result of an arithmetic operation between two operands is described above, with the exception of
/
, the__truediv__
operation, which always returnsfloat32
orfloat64
type.Note
The only allowed arithmetic operation between two
bool
values is multiplication(*)
.- Return type:
TensorList of the type that is calculated based on the type promotion rules.
- Comparison operations: ==, !=, <, <=, >, >=
Comparison operations.
- Return type:
TensorList of
bool
type.
- Bitwise binary operations: &, |, ^
The bitwise binary operations follow the same type promotion rules as arithmetic binary operations, but their inputs are restricted to integral types (including
bool
).Note
A bitwise operation can be applied to two boolean inputs. Those operations can be used to emulate element-wise logical operations on Tensors.
- Return type:
TensorList of the type that is calculated based on the type promotion rules.
Broadcasting#
The term “broadcasting” refers to how tensors with different shapes are treated in mathematical expressions. A value from a smaller tensor is “broadcast” so it contributes to multiple output values. At its simplest, a scalar value is broadcast to all output values. In more complex cases, the values can be broadcast along some dimensions if one of the operands has size 1 and the other is larger:
[[D, E], [[ A+D, B+E ],
[[A, B]] + [F, G], == [ A+F, B+G ],
[H, J]] [ A+H, B+J ]]
In the example above, the operands have shapes of (1, 2) and (3, 2). The values from the array [[A, B]] are broadcast along axis 0. It’s possible that both operands are subject to broadcasting along different dimensions:
[[D], [[ A+D, B+D ],
[[A, B ]] + [E], == [ A+E, B+E ],
[F]] [ A+F, B+F ]]
In this example, the shapes are (1, 2) and (3, 1) - the first operand is broadcast along axis 0 and the second is broadcast along axis 1.
Shape extension#
For convenience, if the arrays have different number of dimensions, the shapes are padded with outer unit dimensions:
shape of A == (480, 640, 3)
shape of B == (3)
shape of A + B == (480, 640, 3) # b is treated as if it was shaped (1, 1, 3)
Limitations#
The broadcasting operations in DALI can have only limited complexity. When broadcasting, the adjacent axes that need or do not need broadcasting are grouped. There can be up to six alternating broadcast/non-broadcast groups. Example of grouping:
shape of A == a, b, 1, c, d
shape of B == a, b, e, 1, 1
grouping dimensions (0..1) and (3..4)
grouped shapes:
a*b, 1, c*d
a*b, e, 1
Mathematical Functions#
Similarly to arithmetic expressions, one can use selected mathematical functions in the Pipeline
graph definition. They also accept nvidia.dali.pipeline.DataNode
,
nvidia.dali.types.Constant()
or regular Python value of type bool
, int
, or float
as arguments. At least one of the inputs must be the output of other DALI Operator.
- nvidia.dali.math.abs(input)#
Computes absolute value of values in
input
.- Return type:
TensorList of abs(input). The type is preserved.
- nvidia.dali.math.fabs(input)#
Computes float absolute value of values in
input
.- Return type:
TensorList of fabs(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.floor(input)#
Computes floor of values in
input
.- Return type:
TensorList of floor(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.ceil(input)#
Computes ceil of values in
input
.- Return type:
TensorList of ceil(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.pow(base, exponent)#
Computes base to the power of exponents, that is base ** exponent.
- Return type:
TensorList of pow(base, exponent). Type is calculated based on the type promotion rules.
- nvidia.dali.math.fpow(base, exponent)#
Computes base to the power of exponents as floating point numbers.
- Return type:
TensorList of pow(base, exponent). If all inputs are integers, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.min(left, right)#
Computes minima of corresponding values in
left
andright
.- Return type:
TensorList of the type that is calculated based on the type promotion rules.
- nvidia.dali.math.max(left, right)#
Computes maxima of corresponding values in
left
andright
.- Return type:
TensorList of the type that is calculated based on the type promotion rules.
- nvidia.dali.math.clamp(value, lo, hi)#
Produces a tensor of values from
value
clamped to the range[lo, hi]
.- Return type:
TensorList of the type that is calculated based on the type promotion rules.
Exponents and logarithms#
- nvidia.dali.math.sqrt(input)#
Computes square root of values in
input
.- Return type:
TensorList of sqrt(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.rsqrt(input)#
Computes reciprocal of the square root of values in
input
.- Return type:
TensorList of rsqrt(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.cbrt(input)#
Computes cube root of values in
input
.- Return type:
TensorList of cbrt(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.exp(input)#
Computes exponential of values in
input
.- Return type:
TensorList of exp(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.log(input)#
Computes natural logarithm (base-e) of values in
input
.- Return type:
TensorList of log(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.log2(input)#
Computes logarithm (base-2) of values in
input
.- Return type:
TensorList of log2(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.log10(input)#
Computes logarithm (base-10) of values in
input
.- Return type:
TensorList of log10(input). If input is an integer, the result will be float, otherwise the type is preserved.
Trigonometric Functions#
- nvidia.dali.math.sin(input)#
Computes sine of values in
input
.- Return type:
TensorList of sin(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.cos(input)#
Computes cosine of values in
input
.- Return type:
TensorList of cos(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.tan(input)#
Computes tangent of values in
input
.- Return type:
TensorList of tan(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.asin(input)#
Computes arcus sine of values in
input
.- Return type:
TensorList of asin(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.acos(input)#
Computes arcus cosine of values in
input
.- Return type:
TensorList of acos(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.atan(input)#
Computes arcus tangent of values in
input
.- Return type:
TensorList of atan(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.atan2(x, y)#
Computes arcus tangent of corresponding values in x / y.
- Return type:
TensorList of atan2(x, y). If all inputs are integers, the result will be float, otherwise the type is preserved.
Hyperbolic Functions#
- nvidia.dali.math.sinh(input)#
Computes hyperbolic sine of values in
input
.- Return type:
TensorList of sinh(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.cosh(input)#
Computes hyperbolic cosine of values in
input
.- Return type:
TensorList of cosh(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.tanh(input)#
Computes hyperbolic tangent of values in
input
.- Return type:
TensorList of tanh(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.asinh(input)#
Computes inverse hyperbolic sine of values in
input
.- Return type:
TensorList of asinh(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.acosh(input)#
Computes inverse hyperbolic cosine of values in
input
.- Return type:
TensorList of acosh(input). If input is an integer, the result will be float, otherwise the type is preserved.
- nvidia.dali.math.atanh(input)#
Computes inverse hyperbolic tangent of values in
input
.- Return type:
TensorList of atanh(input). If input is an integer, the result will be float, otherwise the type is preserved.