[TOPI][OP]change float multiplication of resize op to integer division
This PR is aim to change floating point multiplication of resize operator to integer division when method is nearest_neighbor and coordinate_transformation_mode is asymmetric.
Why this change?
When using create_prim_func to convert topi.upsampling into primfunc, tir analyzer cannot parse complex floating point operations in tir block body. In the following case, the dimensions of read buffer are inferred as 0 : 128. We expect to be tir.reads([x[i0_1, i1_1, i2_1/scale_h, i3_1/scale_w]])in line 18,so it can perform compute_at schedule on this prim_func.
scale_h = 2
scale_w = 2
x = te.placeholder([1, 128, 128, 128], "int8", "x")
y = topi.nn.upsampling(x, scale_h , scale_w)
func = te.create_prim_func([x, y])
print(func)
primfn(var_x: handle, var_resize: handle) -> ()
attr = {"global_symbol": "main", "tir.noalias": True}
buffers = {x: Buffer(x_1: Pointer(global int8), int8, [1, 128, 128, 128], []),
resize: Buffer(resize_1: Pointer(global int8), int8, [1, 128, 256, 256], [])}
buffer_map = {var_x: x, var_resize: resize} {
block([], "root") {
tir.reads([])
tir.writes([])
for (i0: int32, 0, 1) {
for (i1: int32, 0, 128) {
for (i2: int32, 0, 256) {
for (i3: int32, 0, 256) {
block([1, 128, 256, 256], "resize") as [i0_1, i1_1, i2_1, i3_1] {
bind(i0_1, i0)
bind(i1_1, i1)
bind(i2_1, i2)
bind(i3_1, i3)
tir.reads([x[i0_1, i1_1, 0:128, 0:128]])
tir.writes([resize[i0_1, i1_1, i2_1, i3_1]])
resize[i0_1, i1_1, i2_1, i3_1] = cast(int8, cast(float32, x[i0_1, i1_1, max(min(cast(int32, @tir.floor(((0.5f32*cast(float32, i2_1)) + 1e-05f32), dtype=float32)), 127), 0), max(min(cast(int32, @tir.floor(((0.5f32*cast(float32, i3_1)) + 1e-05f32), dtype=float32)), 127), 0)]))
}
}
}
}
}
Why can it be changed?
This PR only modify float multiplication to integer division when method is nearest_neighbor and coordinate_transformation_mode is asymmetric(it's default setting for topi.upsampling op).
Go further, we expect to convert $\lfloor s * x + \epsilon_1 \rfloor$ to $x//a$, $s$ is scale, a float number, $x$ is input, $\epsilon_1$ is 1e-5 in resize op, $a$ is the reciprocal of $s$, an integer.
So, we can represent output as this: $y = \lfloor s * x + \epsilon_1 \rfloor = \lfloor ( \frac{1}{a} + \epsilon_0 ) * x + \epsilon_1 \rfloor = \lfloor \frac{x}{a} + \epsilon_0 * x + \epsilon_1 \rfloor$, let $x = a * m + n$, where $m = x // a, n = x % a$ , then we can get $y = m + \lfloor \frac{n}{a} + \epsilon_0 * x + \epsilon_1 \rfloor$. In the case of upsampling op, $x$ and $a$ are always integers, so inequality $\frac{n}{a} \le \frac{a-1}{a}$ always holds. If $\epsilon_0 * x + \epsilon_1 < \frac{1}{a}$, we can get $0 < \frac{n}{a} + \epsilon_0 * x + \epsilon_1 < 1$,then $y = m + \lfloor \frac{n}{a} + \epsilon_0 * x + \epsilon_1 \rfloor = m = x // a$.
To sum up, when $\epsilon_0 * x + \epsilon_1 < \frac{1}{a}$, we can get $y = \lfloor s*x + \epsilon_1 \rfloor = x // a$.
In this PR, we assume $\epsilon_0 = \epsilon_1 = 1e-5$ and can_convert_multiply_to_intdivto check $\epsilon_0 * x + \epsilon_1 < \frac{1}{a}$.
cc @masahi