I have been working on some tests, and have found a couple potential issues with relay.analysis.alpha_equal
. Here are two examples to illustrate what I’m seeing:
- This first test creates two functions. They are computationally equivalent, but
add_1_fn
has an attribute set. When I runalpha_equal
, I expect the result to beFalse
. However, the result is different depending on the order of the inputs. Whenadd_1_fn
is passed as LHS andadd_fn
is passed as RHS, the result isTrue
.
from tvm import relay
from tvm.relay.testing import run_opt_pass
import tvm.tir
def test_fn_attribute():
# create function that performs add
a = relay.var('a', shape=(10, 10))
b = relay.var('b', shape=(10, 10))
add = relay.add(a, b)
add_fn = relay.Function([a, b], add)
add_fn = run_opt_pass(add_fn, relay.transform.InferType())
# create function that performs add with test attribute
c = relay.var('c', shape=(10, 10))
d = relay.var('d', shape=(10, 10))
add_1 = relay.add(c, d)
add_1_fn = relay.Function([c, d], add_1)
add_1_fn = add_1_fn.set_attribute("TestAttribute", tvm.tir.StringImm("test"))
add_1_fn = run_opt_pass(add_1_fn, relay.transform.InferType())
print(relay.analysis.alpha_equal(add_fn, add_1_fn)) # should be false
print(relay.analysis.alpha_equal(add_1_fn, add_fn)) # should be false
test_fn_attribute()
Output:
False
True
Relay graphs:
# add_fn
fn (%a: Tensor[(10, 10), float32], %b: Tensor[(10, 10), float32]) -> Tensor[(10, 10), float32] {
add(%a, %b) /* ty=Tensor[(10, 10), float32] */
}
# add_1_fn
fn (%c: Tensor[(10, 10), float32], %d: Tensor[(10, 10), float32], TestAttribute="test") -> Tensor[(10, 10), float32] {
add(%c, %d) /* ty=Tensor[(10, 10), float32] */
}
-
The second example creates two graphs that are again computationally equivalent, but have different dataflow. There are two key differences in the graphs:
- The second function,
add_6_fn
, contains a redundantadd
operation. This redundant operation is used in computing the final output. - The final computation in each function,
add_2
andadd_6
, are defined with their relative operands flipped.
When I run
alpha_equal
, I expect the result to beFalse
. However, the result is again different depending on the order of the inputs. Whenadd_6_fn
is passed as LHS andadd_2_fn
is passed as RHS, the result isTrue
. - The second function,
from tvm import relay
from tvm.relay.testing import run_opt_pass
import tvm.tir
def test_dataflow():
# create function that performs three adds
a = relay.var('a', shape=(10, 10))
b = relay.var('b', shape=(10, 10))
add = relay.add(a, b)
add_1 = relay.add(a, add)
add_2 = relay.add(add_1, add)
add_2_fn = relay.Function([a, b], add_2)
add_2_fn = run_opt_pass(add_2_fn, relay.transform.InferType())
# create function that performs four adds but is computationally equivalent
c = relay.var('c', shape=(10, 10))
d = relay.var('d', shape=(10, 10))
add_3 = relay.add(c, d)
add_4 = relay.add(c, add_3)
add_5 = relay.add(c, d)
add_6 = relay.add(add_4, add_5)
add_6_fn = relay.Function([c, d], add_6)
add_6_fn = run_opt_pass(add_6_fn, relay.transform.InferType())
print(relay.analysis.alpha_equal(add_2_fn, add_6_fn)) # should be false
print(relay.analysis.alpha_equal(add_6_fn, add_2_fn)) # should be false
test_dataflow()
Output:
False
True
Relay graphs:
# add_2_fn
fn (%a: Tensor[(10, 10), float32], %b: Tensor[(10, 10), float32]) -> Tensor[(10, 10), float32] {
%0 = add(%a, %b) /* ty=Tensor[(10, 10), float32] */;
%1 = add(%a, %0) /* ty=Tensor[(10, 10), float32] */;
add(%1, %0) /* ty=Tensor[(10, 10), float32] */
}
# add_6_fn
fn (%c: Tensor[(10, 10), float32], %d: Tensor[(10, 10), float32]) -> Tensor[(10, 10), float32] {
%0 = add(%c, %d) /* ty=Tensor[(10, 10), float32] */;
%1 = add(%c, %0) /* ty=Tensor[(10, 10), float32] */;
%2 = add(%c, %d) /* ty=Tensor[(10, 10), float32] */;
add(%1, %2) /* ty=Tensor[(10, 10), float32] */
}
Interestingly, when add_2
is defined as relay.add(add, add_1)
, both outputs are correctly False
.
I have a couple of questions based on this:
- Is this expected behavior?
- What is the correct ordering of inputs for
alpha_equal
? Shouldexpected
be LHS andactual
be RHS? From my testing, it seems that this is correct.
This PR has some initial discussion on the issue.
Thanks!