Description
The noop_elimination pass removes floor_div(x, y=1) as an identity, but floor_div rounds toward negative infinity, so floor_div(x, 1) == floor(x) != x for non-integer float inputs. The pass therefore silently drops the floor and changes the model's numerics — no error, just wrong results. noop_elimination is in the common (default) pipeline.
Reproduction
from coremltools.converters.mil.mil import Builder as mb
from coremltools.converters.mil.mil.passes.pass_registry import PASS_REGISTRY
@mb.program(input_specs=[mb.TensorSpec(shape=(4,))])
def prog(x):
r = mb.floor_div(x=x, y=1.0)
return mb.add(x=r, y=1.0)
PASS_REGISTRY["common::noop_elimination"].apply(prog)
print([op.op_type for op in prog.functions["main"].operations])
# ['add'] -- floor_div silently removed; the model now computes x + 1 instead of floor(x) + 1
For x = [1.5, -0.5, 2.9, -2.1] the original graph computes floor(x) + 1 = [2, 0, 3, -1], but after the pass it computes x + 1 = [2.5, 0.5, 3.9, -1.1].
Cause
In noop_elimination.py, remove_elementwise groups floor_div with real_div and pow:
elif op.op_type in {"floor_div", "pow", "real_div"}:
return _remove_elementwise_binary(op, None, 1) # remove when divisor/exponent == 1
x / 1 == x and x ** 1 == x hold for all reals, but floor_div(x, 1) == floor(x) equals x only for integer x. floor_div is defined for fp16/fp32/i32 and there is no dtype guard, so the floor is dropped for float inputs.
Expected
floor_div(x, 1) should be removed only when x is integral, and kept for float inputs. PR incoming.
Description
The
noop_eliminationpass removesfloor_div(x, y=1)as an identity, butfloor_divrounds toward negative infinity, sofloor_div(x, 1) == floor(x) != xfor non-integer float inputs. The pass therefore silently drops the floor and changes the model's numerics — no error, just wrong results.noop_eliminationis in the common (default) pipeline.Reproduction
For
x = [1.5, -0.5, 2.9, -2.1]the original graph computesfloor(x) + 1 = [2, 0, 3, -1], but after the pass it computesx + 1 = [2.5, 0.5, 3.9, -1.1].Cause
In
noop_elimination.py,remove_elementwisegroupsfloor_divwithreal_divandpow:x / 1 == xandx ** 1 == xhold for all reals, butfloor_div(x, 1) == floor(x)equalsxonly for integerx.floor_divis defined for fp16/fp32/i32 and there is no dtype guard, so the floor is dropped for float inputs.Expected
floor_div(x, 1)should be removed only whenxis integral, and kept for float inputs. PR incoming.