Repository: uxlfoundation/oneDNN
Pull request #3166: graph: utils: pm: support commutative select
Author: login: ShanSimu
Labels: name: component:graph-api
description: Codeowner: @oneapi-src/onednn-graph
name: component:tests
description: Codeowner: @oneapi-src/onednn-arch
Created: 2025-04-25T02:16:14Z | Merged: 2025-05-15T12:15:44Z

# Description

The original setting for "commutative" was limited to operators with only two inputs. However, the select operator has three inputs, and only two of them are commutative.
A new member variable, `std::set<size_t> commutative_inputs`, has been added to `op_schema` to store the indices of commutative inputs. The function `set_commutative_inputs(std::set<size_t> inputs = {0, 1})` uses default parameters to maintain support for the original commutative operators.
In `nested_matcher.cpp`, the functions `has_commutative_inputs`, `match_commutative_inputs`, and `match_node_inputs` have been updated to include checks for whether a port belongs to the commutative inputs.

This PR is for fix MFDNN-13383. Another method is also implemented [here](https://github.com/uxlfoundation/oneDNN/tree/shaojiec/select_input_extend), which is achieved by swapping the mapping method of ports inside and outside the subgraph.


Diff:
diff --git a/src/graph/interface/op_def.hpp b/src/graph/interface/op_def.hpp
index 2a04da5eb3d..78fce3a6570 100644
--- a/src/graph/interface/op_def.hpp
+++ b/src/graph/interface/op_def.hpp
@@ -53,7 +53,7 @@ DNNL_GRAPH_OP_SCHEMA(Add, 1,
         op_schema_t()
                 .set_num_inputs(2)
                 .set_num_outputs(1)
-                .set_commutative_inputs()
+                .set_commutative_inputs({0, 1})
                 .set_input(0, "src_0", "T1")
                 .set_input(1, "src_1", "T2")
                 .set_output(0, "dst", "T3")
@@ -704,7 +704,7 @@ DNNL_GRAPH_OP_SCHEMA(Maximum, 1,
         op_schema_t()
                 .set_num_inputs(2)
                 .set_num_outputs(1)
-                .set_commutative_inputs()
+                .set_commutative_inputs({0, 1})
                 .set_input(0, "src_0", "T")
                 .set_input(1, "src_1", "T")
                 .set_output(0, "dst", "T")
@@ -766,7 +766,7 @@ DNNL_GRAPH_OP_SCHEMA(Minimum, 1,
         op_schema_t()
                 .set_num_inputs(2)
                 .set_num_outputs(1)
-                .set_commutative_inputs()
+                .set_commutative_inputs({0, 1})
                 .set_input(0, "src_0", "T")
                 .set_input(1, "src_1", "T")
                 .set_output(0, "dst", "T")
@@ -802,7 +802,7 @@ DNNL_GRAPH_OP_SCHEMA(Multiply, 1,
         op_schema_t()
                 .set_num_inputs(2)
                 .set_num_outputs(1)
-                .set_commutative_inputs()
+                .set_commutative_inputs({0, 1})
                 .set_input(0, "src_0", "T1")
                 .set_input(1, "src_1", "T2")
                 .set_output(0, "dst", "T3")
@@ -999,6 +999,7 @@ DNNL_GRAPH_OP_SCHEMA(Select, 1,
         op_schema_t()
                 .set_num_inputs(3)
                 .set_num_outputs(1)
+                .set_commutative_inputs({1, 2})
                 .set_input(0, "cond", "T1")
                 .set_input(1, "src_0", "T2")
                 .set_input(2, "src_1", "T2")
diff --git a/src/graph/interface/op_schema.cpp b/src/graph/interface/op_schema.cpp
index 341835071f8..56931216336 100644
--- a/src/graph/interface/op_schema.cpp
+++ b/src/graph/interface/op_schema.cpp
@@ -102,15 +102,27 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
-    commutative_inputs_enabled_ = true;
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::set<size_t> &inputs) {
+    assertm(inputs.size() == 2, "the number of commutative inputs must be 2");
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
+    commutative_inputs_ = inputs;
     return *this;
 }
 
-bool op_schema_t::get_commutative_inputs() const {
-    return commutative_inputs_enabled_;
+bool op_schema_t::is_commutative_inputs(
+        const size_t input0, const size_t input1) const {
+
+    return is_commutative_op() && commutative_inputs_.count(input0)
+            && commutative_inputs_.count(input1);
+}
+
+bool op_schema_t::is_commutative_op() const {
+    return !commutative_inputs_.empty();
 }
 
 op_schema_t &op_schema_t::set_type_constraints(
diff --git a/src/graph/interface/op_schema.hpp b/src/graph/interface/op_schema.hpp
index 8662d0e99f2..198823f2466 100644
--- a/src/graph/interface/op_schema.hpp
+++ b/src/graph/interface/op_schema.hpp
@@ -167,11 +167,14 @@ class op_schema_t {
     op_schema_t &set_output(size_t out_offset, std::string &&out_name,
             std::string &&dtype_string = "any");
 
-    /*! @brief Enable commutative inputs */
-    op_schema_t &set_commutative_inputs();
+    /*! @brief Enable commutative inputs for a specific pair of inputs*/
+    op_schema_t &set_commutative_inputs(const std::set<size_t> &inputs);
 
-    /*! @brief Get whether the commutative inputs option is enabled or not */
-    bool get_commutative_inputs() const;
+    /*! @brief Check whether the inputs are both commutative inputs.*/
+    bool is_commutative_inputs(const size_t input0, const size_t input1) const;
+
+    /*! @brief Check whether the op has commutative inputs*/
+    bool is_commutative_op() const;
 
     op_schema_t &set_type_constraints(
             std::string &&dtype_string, std::set<data_type_t> &&dtypes);
@@ -300,7 +303,7 @@ class op_schema_t {
     std::unordered_map<op_attr_t, attribute_t> attributes_;
     shape_infer_fn tensor_inference_function_ = nullptr;
     std::vector<op_def_constraint_fn> op_def_constraint_functions_;
-    bool commutative_inputs_enabled_ = false;
+    std::set<size_t> commutative_inputs_;
     // type erased key-value storage
     std::unordered_map<std::string, utils::any_t> additional_items_map_;
 };
diff --git a/src/graph/utils/pm/nested_matcher.cpp b/src/graph/utils/pm/nested_matcher.cpp
index a0586403ca4..81a1647c3de 100644
--- a/src/graph/utils/pm/nested_matcher.cpp
+++ b/src/graph/utils/pm/nested_matcher.cpp
@@ -69,11 +69,18 @@ namespace utils {
 namespace pm {
 
 namespace {
-// check if an op's inputs are commutative
-bool has_commutative_inputs(op_t *op) {
+// check if an op has commutative inputs
+bool is_commutative_op(op_t *op) {
     const op_schema_t *opm
             = op_schema_registry_t::get_op_schema(op->get_kind());
-    return opm->get_commutative_inputs();
+    return opm->is_commutative_op();
+}
+
+// check if an op's specific inputs are commutative
+bool is_commutative_inputs(op_t *op, const size_t input0, const size_t input1) {
+    const op_schema_t *opm
+            = op_schema_registry_t::get_op_schema(op->get_kind());
+    return opm->is_commutative_inputs(input0, input1);
 }
 
 // fill local context in map when optional exists
@@ -208,6 +215,9 @@ bool node_inputs_matcher_t::match_commutative_inputs() {
                 ++op_input_offset) {
             if (verified_op_input_ports.find(op_input_offset)
                             == verified_op_input_ports.end()
+                    && (node_input_offset == op_input_offset
+                            || is_commutative_inputs(get_op(),
+                                    node_input_offset, op_input_offset))
                     && match_input_by_offset(
                             op_input_offset, node_input_offset)) {
                 verified_op_input_ports.insert(op_input_offset);
@@ -252,7 +262,7 @@ bool match_node_inputs(const binding_t &b, match_context_t *ctx,
     if (node_inputs_matcher.get_node()->get_inputs().size()
             == VARIADIC_INPUT_NUM) {
         matching_status = node_inputs_matcher.match_variadic_inputs();
-    } else if (!has_commutative_inputs(node_inputs_matcher.get_op())) {
+    } else if (!is_commutative_op(node_inputs_matcher.get_op())) {
         matching_status = node_inputs_matcher.match_non_commutative_inputs();
     } else {
         matching_status = node_inputs_matcher.match_commutative_inputs();
@@ -552,7 +562,8 @@ bool match_node(const binding_t &b, match_context_t *ctx,
                 __FILE__, __LINE__);
         return false;
     }
-    if (!has_commutative_inputs(b.bind_op) && b.bind_op_port != b.bind_port) {
+    if (b.bind_op_port != b.bind_port
+            && !is_commutative_inputs(b.bind_op, b.bind_op_port, b.bind_port)) {
         DEBUG(DEBUGINFO_PM,
                 "matching op & node: %s (%s) <=> %s, matching "
                 "failed \n",
diff --git a/tests/gtests/graph/unit/utils/test_pattern_matcher_cpu.cpp b/tests/gtests/graph/unit/utils/test_pattern_matcher_cpu.cpp
index 59b9a373973..4ac16ba2594 100644
--- a/tests/gtests/graph/unit/utils/test_pattern_matcher_cpu.cpp
+++ b/tests/gtests/graph/unit/utils/test_pattern_matcher_cpu.cpp
@@ -1108,6 +1108,53 @@ TEST(test_utils_pattern_matcher, OptionalWithLargerPort) {
     ASSERT_EQ(fusion_ops.size(), 3U);
 }
 
+TEST(test_utils_pattern_matcher, CommutativeSelect) {
+    auto graphp = std::make_shared<pb_graph_t>();
+    //    GreaterEqual matmul add
+    //             \     /    /
+    //                Select
+    auto pmatmul = graphp->append_op(MatMul);
+    auto pgreaterequal = graphp->append_op(GreaterEqual);
+    auto padd = graphp->append_op(Add);
+    auto pselect = graphp->append_op(Select,
+            {in_edge(0, pgreaterequal, 0), in_edge(1, pmatmul, 0),
+                    in_edge(2, padd, 0)});
+    UNUSED(pselect);
+
+    graph_t agraph;
+    op_t greaterEqual {0, GreaterEqual, "greaterEqual"};
+    op_t matmul {1, MatMul, "matmul"};
+    op_t add {2, Add, "add"};
+    op_t select {3, Select, "select"};
+
+    std::vector<logical_tensor_t> lt_vec = create_logical_tensors(10);
+    lt_vec[2].data_type = data_type::boolean;
+
+    greaterEqual.add_input(lt_vec[0]);
+    greaterEqual.add_input(lt_vec[1]);
+    greaterEqual.add_output(lt_vec[2]);
+    matmul.add_input(lt_vec[3]);
+    matmul.add_input(lt_vec[4]);
+    matmul.add_output(lt_vec[5]);
+    add.add_input(lt_vec[6]);
+    add.add_input(lt_vec[7]);
+    add.add_output(lt_vec[8]);
+    select.add_input(lt_vec[2]);
+    select.add_input(lt_vec[8]);
+    select.add_input(lt_vec[5]);
+    select.add_output(lt_vec[9]);
+
+    ASSERT_EQ(agraph.add_op(&matmul), status::success);
+    ASSERT_EQ(agraph.add_op(&greaterEqual), status::success);
+    ASSERT_EQ(agraph.add_op(&add), status::success);
+    ASSERT_EQ(agraph.add_op(&select), status::success);
+    agraph.finalize();
+
+    std::vector<op_t *> fusion_ops;
+    EXPECT_TRUE(match_pattern(agraph.get_ops()[0].get(), graphp, fusion_ops));
+    ASSERT_EQ(fusion_ops.size(), 4U);
+}
+
 //
 // ?: means optional
 // ^: means repetition


Reviews:
user: login: ElaineBao
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T05:16:14Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-25T06:21:30Z
user: login: ElaineBao
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T06:28:04Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-25T06:28:25Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-25T06:28:35Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-25T06:28:43Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-25T06:28:50Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-25T06:45:33Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-25T08:18:03Z
user: login: wzt1997
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-27T07:44:45Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-27T08:01:37Z
user: login: ElaineBao
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-27T09:27:00Z
user: login: ElaineBao
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-27T09:28:48Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-28T07:53:52Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-28T08:00:08Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-29T03:28:17Z
user: login: ElaineBao
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-29T04:42:21Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-29T05:14:59Z
user: login: ElaineBao
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T05:10:52Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-30T05:21:05Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-30T05:21:47Z
user: login: ElaineBao
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-30T05:23:18Z
user: login: Jiexin-Zheng
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T12:51:09Z
user: login: TaoLv
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T01:36:57Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-09T01:43:29Z
user: login: TaoLv
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T02:04:01Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-09T02:18:53Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-09T02:25:31Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-09T02:27:09Z
user: login: TaoLv
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T02:32:00Z
user: login: TaoLv
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-09T02:36:49Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-09T03:07:47Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-09T03:08:32Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-09T03:08:47Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-09T03:21:10Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-09T11:17:46Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-09T11:18:50Z
user: login: TaoLv
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T12:05:29Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-14T01:56:05Z
user: login: TaoLv
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T03:05:39Z
user: login: TaoLv
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T03:06:19Z
user: login: ShanSimu
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-14T07:29:51Z
user: login: TaoLv
body: Please rebase and test the PR.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T02:38:59Z

Review comments:
diff_hunk: @@ -102,15 +102,24 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(std::set<size_t> inputs) {
path: src/graph/interface/op_schema.cpp
user: login: ElaineBao
body: ```suggestion
op_schema_t &op_schema_t::set_commutative_inputs(const std::set<size_t> &inputs) {
```
created_at: 2025-04-25T05:01:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -102,15 +102,24 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(std::set<size_t> inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of commutative inputs is at least two");
+    size_t max_index = *(num_inputs_.begin());
+    for (size_t idx : inputs) {
+        assertm(idx < max_index,
+                "each input value must be less than input numbers");
+    }
     commutative_inputs_enabled_ = true;
+    commutative_inputs = inputs;
     return *this;
 }
 
-bool op_schema_t::get_commutative_inputs() const {
-    return commutative_inputs_enabled_;
+bool op_schema_t::get_commutative_inputs(std::set<size_t> inputs) const {
path: src/graph/interface/op_schema.cpp
user: login: ElaineBao
body: ```suggestion
bool op_schema_t::get_commutative_inputs(const std::set<size_t> &inputs) const {
```
created_at: 2025-04-25T05:04:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,10 +70,10 @@ namespace pm {
 
 namespace {
 // check if an op's inputs are commutative
-bool has_commutative_inputs(op_t *op) {
+bool has_commutative_inputs(op_t *op, std::set<size_t> inputs) {
path: src/graph/utils/pm/nested_matcher.cpp
user: login: ElaineBao
body: ```suggestion
bool has_commutative_inputs(op_t *op, const std::set<size_t> &inputs) {
```
created_at: 2025-04-25T05:04:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -252,7 +254,8 @@ bool match_node_inputs(const binding_t &b, match_context_t *ctx,
     if (node_inputs_matcher.get_node()->get_inputs().size()
             == VARIADIC_INPUT_NUM) {
         matching_status = node_inputs_matcher.match_variadic_inputs();
-    } else if (!has_commutative_inputs(node_inputs_matcher.get_op())) {
+    } else if (!has_commutative_inputs(node_inputs_matcher.get_op(),
+                       {b.bind_op_port, b.bind_port})) {
path: src/graph/utils/pm/nested_matcher.cpp
user: login: ElaineBao
body: I'm wondering, for select, whose first input is non-commutative, and second & third inputs are commutative, how can we make sure all the inputs are correctly visited and matched?
created_at: 2025-04-25T05:11:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -200,12 +200,14 @@ bool node_inputs_matcher_t::match_commutative_inputs() {
 
     for (size_t node_input_offset = 0; node_input_offset < node_inputs_.size();
             ++node_input_offset) {
+        if (!has_commutative_inputs(get_op(), {node_input_offset})) continue;
path: src/graph/utils/pm/nested_matcher.cpp
user: login: ElaineBao
body: maybe it's better to change the name of this function to is_commutative_input(), as it's verifying the single input port now.
created_at: 2025-04-25T05:13:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -252,7 +254,8 @@ bool match_node_inputs(const binding_t &b, match_context_t *ctx,
     if (node_inputs_matcher.get_node()->get_inputs().size()
             == VARIADIC_INPUT_NUM) {
         matching_status = node_inputs_matcher.match_variadic_inputs();
-    } else if (!has_commutative_inputs(node_inputs_matcher.get_op())) {
+    } else if (!has_commutative_inputs(node_inputs_matcher.get_op(),
+                       {b.bind_op_port, b.bind_port})) {
path: src/graph/utils/pm/nested_matcher.cpp
user: login: ShanSimu
body: When `bind_node` is `select`, `bind_port` is 2 and `bind_op_port` is 1 which conform to `commutative inputs = {1, 2}`.









created_at: 2025-04-25T06:21:30Z
author_association: NONE
diff_hunk: @@ -252,7 +254,8 @@ bool match_node_inputs(const binding_t &b, match_context_t *ctx,
     if (node_inputs_matcher.get_node()->get_inputs().size()
             == VARIADIC_INPUT_NUM) {
         matching_status = node_inputs_matcher.match_variadic_inputs();
-    } else if (!has_commutative_inputs(node_inputs_matcher.get_op())) {
+    } else if (!has_commutative_inputs(node_inputs_matcher.get_op(),
+                       {b.bind_op_port, b.bind_port})) {
path: src/graph/utils/pm/nested_matcher.cpp
user: login: ElaineBao
body: I understand that. I mean how is the 0-th input matched? As it's non-commutative.
created_at: 2025-04-25T06:28:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -102,15 +102,24 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(std::set<size_t> inputs) {
path: src/graph/interface/op_schema.cpp
user: login: ShanSimu
body: Updated
created_at: 2025-04-25T06:28:25Z
author_association: NONE
diff_hunk: @@ -102,15 +102,24 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(std::set<size_t> inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of commutative inputs is at least two");
+    size_t max_index = *(num_inputs_.begin());
+    for (size_t idx : inputs) {
+        assertm(idx < max_index,
+                "each input value must be less than input numbers");
+    }
     commutative_inputs_enabled_ = true;
+    commutative_inputs = inputs;
     return *this;
 }
 
-bool op_schema_t::get_commutative_inputs() const {
-    return commutative_inputs_enabled_;
+bool op_schema_t::get_commutative_inputs(std::set<size_t> inputs) const {
path: src/graph/interface/op_schema.cpp
user: login: ShanSimu
body: Updated
created_at: 2025-04-25T06:28:35Z
author_association: NONE
diff_hunk: @@ -70,10 +70,10 @@ namespace pm {
 
 namespace {
 // check if an op's inputs are commutative
-bool has_commutative_inputs(op_t *op) {
+bool has_commutative_inputs(op_t *op, std::set<size_t> inputs) {
path: src/graph/utils/pm/nested_matcher.cpp
user: login: ShanSimu
body: Updated
created_at: 2025-04-25T06:28:43Z
author_association: NONE
diff_hunk: @@ -200,12 +200,14 @@ bool node_inputs_matcher_t::match_commutative_inputs() {
 
     for (size_t node_input_offset = 0; node_input_offset < node_inputs_.size();
             ++node_input_offset) {
+        if (!has_commutative_inputs(get_op(), {node_input_offset})) continue;
path: src/graph/utils/pm/nested_matcher.cpp
user: login: ShanSimu
body: Updated
created_at: 2025-04-25T06:28:50Z
author_association: NONE
diff_hunk: @@ -252,7 +254,8 @@ bool match_node_inputs(const binding_t &b, match_context_t *ctx,
     if (node_inputs_matcher.get_node()->get_inputs().size()
             == VARIADIC_INPUT_NUM) {
         matching_status = node_inputs_matcher.match_variadic_inputs();
-    } else if (!has_commutative_inputs(node_inputs_matcher.get_op())) {
+    } else if (!has_commutative_inputs(node_inputs_matcher.get_op(),
+                       {b.bind_op_port, b.bind_port})) {
path: src/graph/utils/pm/nested_matcher.cpp
user: login: ShanSimu
body: Do you mean the first or the second input will swap with the first one? I doubt whether that's a valid graph, since the data type of the 0-th input is boolean after all. As for other cases, like when all three input data types are the same but only two are commutative, I don't think we have such a requirement.
created_at: 2025-04-25T06:45:33Z
author_association: NONE
diff_hunk: @@ -252,7 +254,8 @@ bool match_node_inputs(const binding_t &b, match_context_t *ctx,
     if (node_inputs_matcher.get_node()->get_inputs().size()
             == VARIADIC_INPUT_NUM) {
         matching_status = node_inputs_matcher.match_variadic_inputs();
-    } else if (!has_commutative_inputs(node_inputs_matcher.get_op())) {
+    } else if (!has_commutative_inputs(node_inputs_matcher.get_op(),
+                       {b.bind_op_port, b.bind_port})) {
path: src/graph/utils/pm/nested_matcher.cpp
user: login: ShanSimu
body: I have added gtest for verifying match_node_inputs()
created_at: 2025-04-25T08:18:03Z
author_association: NONE
diff_hunk: @@ -301,6 +302,7 @@ class op_schema_t {
     shape_infer_fn tensor_inference_function_ = nullptr;
     std::vector<op_def_constraint_fn> op_def_constraint_functions_;
     bool commutative_inputs_enabled_ = false;
+    std::set<size_t> commutative_inputs;
path: src/graph/interface/op_schema.hpp
user: login: wzt1997
body: better use `commutative_inputs_`
created_at: 2025-04-27T07:36:24Z
author_association: NONE
diff_hunk: @@ -301,6 +302,7 @@ class op_schema_t {
     shape_infer_fn tensor_inference_function_ = nullptr;
     std::vector<op_def_constraint_fn> op_def_constraint_functions_;
     bool commutative_inputs_enabled_ = false;
+    std::set<size_t> commutative_inputs;
path: src/graph/interface/op_schema.hpp
user: login: ShanSimu
body: Updated
created_at: 2025-04-27T08:01:37Z
author_association: NONE
diff_hunk: @@ -1108,6 +1108,46 @@ TEST(test_utils_pattern_matcher, OptionalWithLargerPort) {
     ASSERT_EQ(fusion_ops.size(), 3U);
 }
 
+TEST(test_utils_pattern_matcher, CommutativeSelect) {
+    auto graphp = std::make_shared<pb_graph_t>();
+    // Pattern that captures
+    // MatMul  Add
+    //   \     /
+    //    Select
+    auto pmatmul = graphp->append_op(MatMul);
path: tests/gtests/graph/unit/utils/test_pattern_matcher_cpu.cpp
user: login: ElaineBao
body: Please add another input for select's 0-th input (e.g. GreaterEqual), we want to know if the three inputs (both commutative and non-commutative) can all be matched
created_at: 2025-04-27T09:26:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1108,6 +1108,46 @@ TEST(test_utils_pattern_matcher, OptionalWithLargerPort) {
     ASSERT_EQ(fusion_ops.size(), 3U);
 }
 
+TEST(test_utils_pattern_matcher, CommutativeSelect) {
+    auto graphp = std::make_shared<pb_graph_t>();
+    // Pattern that captures
+    // MatMul  Add
+    //   \     /
+    //    Select
+    auto pmatmul = graphp->append_op(MatMul);
+    auto aadd = graphp->append_op(Add);
+    auto pselect = graphp->append_op(
+            Select, {in_edge(1, pmatmul, 0), in_edge(2, aadd, 0)});
+    UNUSED(pselect);
+
+    graph_t agraph;
+    op_t matmul {0, MatMul, "matmul"};
+    op_t add {1, Add, "Add"};
+    op_t select {2, Select, "select"};
+
+    std::vector<logical_tensor_t> lt_vec = create_logical_tensors(8);
+    lt_vec[6].data_type = data_type::boolean;
+    matmul.add_input(lt_vec[0]);
+    matmul.add_input(lt_vec[1]);
+    matmul.add_output(lt_vec[2]);
+    add.add_input(lt_vec[3]);
+    add.add_input(lt_vec[4]);
+    add.add_output(lt_vec[5]);
+    select.add_input(lt_vec[6]);
+    select.add_input(lt_vec[5]);
+    select.add_input(lt_vec[2]);
+    select.add_output(lt_vec[7]);
+
+    ASSERT_EQ(agraph.add_op(&matmul), status::success);
+    ASSERT_EQ(agraph.add_op(&add), status::success);
+    ASSERT_EQ(agraph.add_op(&select), status::success);
+    agraph.finalize();
+
+    std::vector<op_t *> fusion_ops;
+    EXPECT_TRUE(match_pattern(agraph.get_ops()[0].get(), graphp, fusion_ops));
path: tests/gtests/graph/unit/utils/test_pattern_matcher_cpu.cpp
user: login: ElaineBao
body: And here we need to match from the select op, to verify our assumption.
created_at: 2025-04-27T09:28:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1108,6 +1108,46 @@ TEST(test_utils_pattern_matcher, OptionalWithLargerPort) {
     ASSERT_EQ(fusion_ops.size(), 3U);
 }
 
+TEST(test_utils_pattern_matcher, CommutativeSelect) {
+    auto graphp = std::make_shared<pb_graph_t>();
+    // Pattern that captures
+    // MatMul  Add
+    //   \     /
+    //    Select
+    auto pmatmul = graphp->append_op(MatMul);
path: tests/gtests/graph/unit/utils/test_pattern_matcher_cpu.cpp
user: login: ShanSimu
body: 0-th input for select has been added. And we started pattern matching from `MatMul` to verify a mix of commutative and non-commutative op.

created_at: 2025-04-28T07:53:52Z
author_association: NONE
diff_hunk: @@ -1108,6 +1108,46 @@ TEST(test_utils_pattern_matcher, OptionalWithLargerPort) {
     ASSERT_EQ(fusion_ops.size(), 3U);
 }
 
+TEST(test_utils_pattern_matcher, CommutativeSelect) {
+    auto graphp = std::make_shared<pb_graph_t>();
+    // Pattern that captures
+    // MatMul  Add
+    //   \     /
+    //    Select
+    auto pmatmul = graphp->append_op(MatMul);
+    auto aadd = graphp->append_op(Add);
+    auto pselect = graphp->append_op(
+            Select, {in_edge(1, pmatmul, 0), in_edge(2, aadd, 0)});
+    UNUSED(pselect);
+
+    graph_t agraph;
+    op_t matmul {0, MatMul, "matmul"};
+    op_t add {1, Add, "Add"};
+    op_t select {2, Select, "select"};
+
+    std::vector<logical_tensor_t> lt_vec = create_logical_tensors(8);
+    lt_vec[6].data_type = data_type::boolean;
+    matmul.add_input(lt_vec[0]);
+    matmul.add_input(lt_vec[1]);
+    matmul.add_output(lt_vec[2]);
+    add.add_input(lt_vec[3]);
+    add.add_input(lt_vec[4]);
+    add.add_output(lt_vec[5]);
+    select.add_input(lt_vec[6]);
+    select.add_input(lt_vec[5]);
+    select.add_input(lt_vec[2]);
+    select.add_output(lt_vec[7]);
+
+    ASSERT_EQ(agraph.add_op(&matmul), status::success);
+    ASSERT_EQ(agraph.add_op(&add), status::success);
+    ASSERT_EQ(agraph.add_op(&select), status::success);
+    agraph.finalize();
+
+    std::vector<op_t *> fusion_ops;
+    EXPECT_TRUE(match_pattern(agraph.get_ops()[0].get(), graphp, fusion_ops));
path: tests/gtests/graph/unit/utils/test_pattern_matcher_cpu.cpp
user: login: ShanSimu
body: It seems impossible to declare `select` first and `edges` at last because edge related functions of `pb_graph_t` are protected. Instead, we started pattern matching from MatMul to verify a mix of commutative and non-commutative op.
created_at: 2025-04-28T08:00:08Z
author_association: NONE
diff_hunk: @@ -1108,6 +1108,46 @@ TEST(test_utils_pattern_matcher, OptionalWithLargerPort) {
     ASSERT_EQ(fusion_ops.size(), 3U);
 }
 
+TEST(test_utils_pattern_matcher, CommutativeSelect) {
+    auto graphp = std::make_shared<pb_graph_t>();
+    // Pattern that captures
+    // MatMul  Add
+    //   \     /
+    //    Select
+    auto pmatmul = graphp->append_op(MatMul);
path: tests/gtests/graph/unit/utils/test_pattern_matcher_cpu.cpp
user: login: ShanSimu
body: The logic of `match_commutative_inputs()` has been modified: when `node_input_offset` and `op_input_offset` are not equal, it will check whether both belong to `commutative_inputs_`. If they do not, `match_input_by_offset()` will not be called.
created_at: 2025-04-29T03:28:17Z
author_association: NONE
diff_hunk: @@ -252,7 +255,7 @@ bool match_node_inputs(const binding_t &b, match_context_t *ctx,
     if (node_inputs_matcher.get_node()->get_inputs().size()
             == VARIADIC_INPUT_NUM) {
         matching_status = node_inputs_matcher.match_variadic_inputs();
-    } else if (!has_commutative_inputs(node_inputs_matcher.get_op())) {
+    } else if (!is_commutative_input(node_inputs_matcher.get_op())) {
path: src/graph/utils/pm/nested_matcher.cpp
user: login: ElaineBao
body: what will happen if this function is called without a set of input ports?
created_at: 2025-04-29T04:42:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -252,7 +255,7 @@ bool match_node_inputs(const binding_t &b, match_context_t *ctx,
     if (node_inputs_matcher.get_node()->get_inputs().size()
             == VARIADIC_INPUT_NUM) {
         matching_status = node_inputs_matcher.match_variadic_inputs();
-    } else if (!has_commutative_inputs(node_inputs_matcher.get_op())) {
+    } else if (!is_commutative_input(node_inputs_matcher.get_op())) {
path: src/graph/utils/pm/nested_matcher.cpp
user: login: ShanSimu
body: It will return directly `commutative_inputs_enabled_`
created_at: 2025-04-29T05:14:59Z
author_association: NONE
diff_hunk: @@ -1108,6 +1108,53 @@ TEST(test_utils_pattern_matcher, OptionalWithLargerPort) {
     ASSERT_EQ(fusion_ops.size(), 3U);
 }
 
+TEST(test_utils_pattern_matcher, CommutativeSelect) {
path: tests/gtests/graph/unit/utils/test_pattern_matcher_cpu.cpp
user: login: ElaineBao
body: The commit message shoud be gtest: graph: ...
created_at: 2025-04-30T05:02:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -167,11 +167,12 @@ class op_schema_t {
     op_schema_t &set_output(size_t out_offset, std::string &&out_name,
             std::string &&dtype_string = "any");
 
-    /*! @brief Enable commutative inputs */
-    op_schema_t &set_commutative_inputs();
+    /*! @brief Enable commutative inputs for specific inputs*/
+    op_schema_t &set_commutative_inputs(
+            const std::set<size_t> &inputs = {0, 1});
 
     /*! @brief Get whether the commutative inputs option is enabled or not */
-    bool get_commutative_inputs() const;
+    bool get_commutative_inputs(const std::set<size_t> &inputs = {}) const;
path: src/graph/interface/op_schema.hpp
user: login: ElaineBao
body: Add a brief description for this function, check if the inputs set are all commutative inputs, if inputs is an empty set, check if the op has commutative inputs.
created_at: 2025-04-30T05:06:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1108,6 +1108,53 @@ TEST(test_utils_pattern_matcher, OptionalWithLargerPort) {
     ASSERT_EQ(fusion_ops.size(), 3U);
 }
 
+TEST(test_utils_pattern_matcher, CommutativeSelect) {
path: tests/gtests/graph/unit/utils/test_pattern_matcher_cpu.cpp
user: login: ShanSimu
body: Updated. Just typo...
created_at: 2025-04-30T05:21:05Z
author_association: NONE
diff_hunk: @@ -167,11 +167,12 @@ class op_schema_t {
     op_schema_t &set_output(size_t out_offset, std::string &&out_name,
             std::string &&dtype_string = "any");
 
-    /*! @brief Enable commutative inputs */
-    op_schema_t &set_commutative_inputs();
+    /*! @brief Enable commutative inputs for specific inputs*/
+    op_schema_t &set_commutative_inputs(
+            const std::set<size_t> &inputs = {0, 1});
 
     /*! @brief Get whether the commutative inputs option is enabled or not */
-    bool get_commutative_inputs() const;
+    bool get_commutative_inputs(const std::set<size_t> &inputs = {}) const;
path: src/graph/interface/op_schema.hpp
user: login: ShanSimu
body: Updated
created_at: 2025-04-30T05:21:47Z
author_association: NONE
diff_hunk: @@ -102,15 +102,23 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::set<size_t> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of commutative inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
     commutative_inputs_enabled_ = true;
+    commutative_inputs_ = inputs;
path: src/graph/interface/op_schema.cpp
user: login: TaoLv
body: Why do we need both `commutative_inputs_enabled_` and `commutative_inputs_`?
created_at: 2025-05-09T01:27:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -102,15 +102,23 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::set<size_t> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of commutative inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
     commutative_inputs_enabled_ = true;
+    commutative_inputs_ = inputs;
path: src/graph/interface/op_schema.cpp
user: login: TaoLv
body: with this, we plan to support only 1 pair of commutative inputs, correct?
created_at: 2025-05-09T01:29:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -102,15 +102,23 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::set<size_t> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of commutative inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
     commutative_inputs_enabled_ = true;
+    commutative_inputs_ = inputs;
     return *this;
 }
 
-bool op_schema_t::get_commutative_inputs() const {
-    return commutative_inputs_enabled_;
+bool op_schema_t::get_commutative_inputs(const std::set<size_t> &inputs) const {
+
+    return commutative_inputs_enabled_
+            && std::includes(commutative_inputs_.begin(),
+                    commutative_inputs_.end(), inputs.begin(), inputs.end());
path: src/graph/interface/op_schema.cpp
user: login: TaoLv
body: Not sure how this function is used. But from it's implementation, i think the function name can be improved:

```
bool op_schema_t::is_commutative_inputs(size_t input1, size_t input2) {
      // make sure both {input1, input2} and {input2, input1} are checked.
      const bool ret = .....;
      return ret;
}
```
created_at: 2025-05-09T01:32:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -167,11 +167,13 @@ class op_schema_t {
     op_schema_t &set_output(size_t out_offset, std::string &&out_name,
             std::string &&dtype_string = "any");
 
-    /*! @brief Enable commutative inputs */
-    op_schema_t &set_commutative_inputs();
+    /*! @brief Enable commutative inputs for specific inputs*/
+    op_schema_t &set_commutative_inputs(
+            const std::set<size_t> &inputs = {0, 1});
path: src/graph/interface/op_schema.hpp
user: login: TaoLv
body: How many places is this function called? If possible, let's remove the default argument and add it explicitly at where it's needed.
created_at: 2025-05-09T01:34:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -167,11 +167,12 @@ class op_schema_t {
     op_schema_t &set_output(size_t out_offset, std::string &&out_name,
             std::string &&dtype_string = "any");
 
-    /*! @brief Enable commutative inputs */
-    op_schema_t &set_commutative_inputs();
+    /*! @brief Enable commutative inputs for specific inputs*/
+    op_schema_t &set_commutative_inputs(
+            const std::set<size_t> &inputs = {0, 1});
 
     /*! @brief Get whether the commutative inputs option is enabled or not */
-    bool get_commutative_inputs() const;
+    bool get_commutative_inputs(const std::set<size_t> &inputs = {}) const;
path: src/graph/interface/op_schema.hpp
user: login: TaoLv
body: The default argument here is confusing.
created_at: 2025-05-09T01:35:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -102,15 +102,23 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::set<size_t> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of commutative inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
     commutative_inputs_enabled_ = true;
+    commutative_inputs_ = inputs;
path: src/graph/interface/op_schema.cpp
user: login: ShanSimu
body: > Why do we need both `commutative_inputs_enabled_` and `commutative_inputs_`?

The commutative_inputs_enabled_ flag can indeed be removed—checking whether commutative_inputs_ is empty is sufficient to determine if commutative behavior is enabled.

> with this, we plan to support only 1 pair of commutative inputs, correct?

We support a set of commutative inputs, not just a single pair.
created_at: 2025-05-09T01:43:28Z
author_association: NONE
diff_hunk: @@ -102,15 +102,23 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::set<size_t> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of commutative inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
     commutative_inputs_enabled_ = true;
+    commutative_inputs_ = inputs;
path: src/graph/interface/op_schema.cpp
user: login: TaoLv
body: what does `a set of commutative inputs` mean? More than two inputs can be commutative? Do you have a test case for that?
I was thinking about an extreme case that input 0 and 1 are commutative and input 2 and 3 are commutative.
created_at: 2025-05-09T02:03:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -102,15 +102,23 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::set<size_t> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of commutative inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
     commutative_inputs_enabled_ = true;
+    commutative_inputs_ = inputs;
path: src/graph/interface/op_schema.cpp
user: login: ShanSimu
body: Yes, `a set of commutative inputs` means more than two inputs can be commutative. We currently don't have such an op or test case.
`input 0 and 1 are commutative and input 2 and 3 are commutative` is another corner case I hadn't considered, and currently there are no corresponding test cases for it.
created_at: 2025-05-09T02:18:53Z
author_association: NONE
diff_hunk: @@ -102,15 +102,23 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::set<size_t> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of commutative inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
     commutative_inputs_enabled_ = true;
+    commutative_inputs_ = inputs;
     return *this;
 }
 
-bool op_schema_t::get_commutative_inputs() const {
-    return commutative_inputs_enabled_;
+bool op_schema_t::get_commutative_inputs(const std::set<size_t> &inputs) const {
+
+    return commutative_inputs_enabled_
+            && std::includes(commutative_inputs_.begin(),
+                    commutative_inputs_.end(), inputs.begin(), inputs.end());
path: src/graph/interface/op_schema.cpp
user: login: ShanSimu
body: In actual usage, one way this function is called is with no parameters, solely to check whether the current op supports commutativity. If we change the parameters to `size_t input1, size_t input2`, it would make this zero-argument usage less straightforward or even impossible without overloading or adding a separate method.
created_at: 2025-05-09T02:25:31Z
author_association: NONE
diff_hunk: @@ -167,11 +167,13 @@ class op_schema_t {
     op_schema_t &set_output(size_t out_offset, std::string &&out_name,
             std::string &&dtype_string = "any");
 
-    /*! @brief Enable commutative inputs */
-    op_schema_t &set_commutative_inputs();
+    /*! @brief Enable commutative inputs for specific inputs*/
+    op_schema_t &set_commutative_inputs(
+            const std::set<size_t> &inputs = {0, 1});
path: src/graph/interface/op_schema.hpp
user: login: ShanSimu
body: There are only four usages relying on the default argument. Removing it would indeed make the function interface clearer and more explicit.
created_at: 2025-05-09T02:27:09Z
author_association: NONE
diff_hunk: @@ -102,15 +102,23 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::set<size_t> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of commutative inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
     commutative_inputs_enabled_ = true;
+    commutative_inputs_ = inputs;
     return *this;
 }
 
-bool op_schema_t::get_commutative_inputs() const {
-    return commutative_inputs_enabled_;
+bool op_schema_t::get_commutative_inputs(const std::set<size_t> &inputs) const {
+
+    return commutative_inputs_enabled_
+            && std::includes(commutative_inputs_.begin(),
+                    commutative_inputs_.end(), inputs.begin(), inputs.end());
path: src/graph/interface/op_schema.cpp
user: login: TaoLv
body: My point is not the arguments of the function. It's more about the function name. You can still use `std::set` to pass the inputs.

- is_commutative_inputs(inputs): check if the given inputs are commutative, return true or false accordingly.
- get_commutative_inputs(): return the set of commutative inputs.
created_at: 2025-05-09T02:32:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -102,15 +102,23 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::set<size_t> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of commutative inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
     commutative_inputs_enabled_ = true;
+    commutative_inputs_ = inputs;
path: src/graph/interface/op_schema.cpp
user: login: TaoLv
body: In this case, I would suggest to stick on what's supported and tested for now. That's only 1 pair of commutative inputs is supported. Update the code and comment to reflect this design and restriction.
For the cases with more than 2 commutative inputs, or two pairs of commutative inputs, we can add support once the request pops up.
created_at: 2025-05-09T02:36:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -102,15 +102,23 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::set<size_t> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of commutative inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
     commutative_inputs_enabled_ = true;
+    commutative_inputs_ = inputs;
path: src/graph/interface/op_schema.cpp
user: login: ShanSimu
body: `commutative_inputs_` has been changed to `std::array<size_t, 2>`, strictly limiting it to two inputs. This change has been documented in the comments to clarify the restriction.

`commutative_inputs_enabled_` has been removed. Since std::array<size_t, 2> has a fixed size of 2, we use a sentinel value—SIZE_MAX—to represent an "empty array" or an uninitialized state of `commutative_inputs_`.

created_at: 2025-05-09T03:07:47Z
author_association: NONE
diff_hunk: @@ -102,15 +102,23 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::set<size_t> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of commutative inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
     commutative_inputs_enabled_ = true;
+    commutative_inputs_ = inputs;
     return *this;
 }
 
-bool op_schema_t::get_commutative_inputs() const {
-    return commutative_inputs_enabled_;
+bool op_schema_t::get_commutative_inputs(const std::set<size_t> &inputs) const {
+
+    return commutative_inputs_enabled_
+            && std::includes(commutative_inputs_.begin(),
+                    commutative_inputs_.end(), inputs.begin(), inputs.end());
path: src/graph/interface/op_schema.cpp
user: login: ShanSimu
body: The function name has been changed to `is_commutative_inputs`
created_at: 2025-05-09T03:08:32Z
author_association: NONE
diff_hunk: @@ -167,11 +167,13 @@ class op_schema_t {
     op_schema_t &set_output(size_t out_offset, std::string &&out_name,
             std::string &&dtype_string = "any");
 
-    /*! @brief Enable commutative inputs */
-    op_schema_t &set_commutative_inputs();
+    /*! @brief Enable commutative inputs for specific inputs*/
+    op_schema_t &set_commutative_inputs(
+            const std::set<size_t> &inputs = {0, 1});
path: src/graph/interface/op_schema.hpp
user: login: ShanSimu
body: Updated
created_at: 2025-05-09T03:08:47Z
author_association: NONE
diff_hunk: @@ -167,11 +167,12 @@ class op_schema_t {
     op_schema_t &set_output(size_t out_offset, std::string &&out_name,
             std::string &&dtype_string = "any");
 
-    /*! @brief Enable commutative inputs */
-    op_schema_t &set_commutative_inputs();
+    /*! @brief Enable commutative inputs for specific inputs*/
+    op_schema_t &set_commutative_inputs(
+            const std::set<size_t> &inputs = {0, 1});
 
     /*! @brief Get whether the commutative inputs option is enabled or not */
-    bool get_commutative_inputs() const;
+    bool get_commutative_inputs(const std::set<size_t> &inputs = {}) const;
path: src/graph/interface/op_schema.hpp
user: login: ShanSimu
body: The default argument here is to maintain compatibility with zero-argument usage(in [nested_matcher.cpp#L73](https://github.com/uxlfoundation/oneDNN/blob/0fc69752f83811c008847ee1ee73076a70244dad/src/graph/utils/pm/nested_matcher.cpp#L73)), where we simply check if the op has commutative inputs. It might make sense to split this into two separate functions:

- is_commutative_inputs(inputs)
- is_commutative_op()
created_at: 2025-05-09T03:21:10Z
author_association: NONE
diff_hunk: @@ -167,11 +167,12 @@ class op_schema_t {
     op_schema_t &set_output(size_t out_offset, std::string &&out_name,
             std::string &&dtype_string = "any");
 
-    /*! @brief Enable commutative inputs */
-    op_schema_t &set_commutative_inputs();
+    /*! @brief Enable commutative inputs for specific inputs*/
+    op_schema_t &set_commutative_inputs(
+            const std::set<size_t> &inputs = {0, 1});
 
     /*! @brief Get whether the commutative inputs option is enabled or not */
-    bool get_commutative_inputs() const;
+    bool get_commutative_inputs(const std::set<size_t> &inputs = {}) const;
path: src/graph/interface/op_schema.hpp
user: login: ShanSimu
body: Updated
created_at: 2025-05-09T11:17:46Z
author_association: NONE
diff_hunk: @@ -102,15 +102,23 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::set<size_t> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of commutative inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
     commutative_inputs_enabled_ = true;
+    commutative_inputs_ = inputs;
     return *this;
 }
 
-bool op_schema_t::get_commutative_inputs() const {
-    return commutative_inputs_enabled_;
+bool op_schema_t::get_commutative_inputs(const std::set<size_t> &inputs) const {
+
+    return commutative_inputs_enabled_
+            && std::includes(commutative_inputs_.begin(),
+                    commutative_inputs_.end(), inputs.begin(), inputs.end());
path: src/graph/interface/op_schema.cpp
user: login: ShanSimu
body: Since the zero-argument usage is separated, the arguments have been changed to `const size_t input0, const size_t input1`
created_at: 2025-05-09T11:18:49Z
author_association: NONE
diff_hunk: @@ -102,15 +102,30 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
-    commutative_inputs_enabled_ = true;
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::array<size_t, 2> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
+    commutative_inputs_ = inputs;
     return *this;
 }
 
-bool op_schema_t::get_commutative_inputs() const {
-    return commutative_inputs_enabled_;
+bool op_schema_t::is_commutative_inputs(
+        const size_t input0, const size_t input1) const {
+
+    return is_commutative_op()
+            && ((commutative_inputs_[0] == input0
+                        && commutative_inputs_[1] == input1)
+                    || (commutative_inputs_[0] == input1
+                            && commutative_inputs_[1] == input0));
+}
+
+bool op_schema_t::is_commutative_op() const {
+    return commutative_inputs_[0] != SIZE_MAX
+            && commutative_inputs_[1] != SIZE_MAX;
path: src/graph/interface/op_schema.cpp
user: login: TaoLv
body: Is it possible to just make it empty to represent that there are no commutative ops?
created_at: 2025-05-13T12:04:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -102,15 +102,30 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
-    commutative_inputs_enabled_ = true;
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::array<size_t, 2> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
+    commutative_inputs_ = inputs;
     return *this;
 }
 
-bool op_schema_t::get_commutative_inputs() const {
-    return commutative_inputs_enabled_;
+bool op_schema_t::is_commutative_inputs(
+        const size_t input0, const size_t input1) const {
+
+    return is_commutative_op()
+            && ((commutative_inputs_[0] == input0
+                        && commutative_inputs_[1] == input1)
+                    || (commutative_inputs_[0] == input1
+                            && commutative_inputs_[1] == input0));
+}
+
+bool op_schema_t::is_commutative_op() const {
+    return commutative_inputs_[0] != SIZE_MAX
+            && commutative_inputs_[1] != SIZE_MAX;
path: src/graph/interface/op_schema.cpp
user: login: ShanSimu
body: `std::array<T, N>` is a fixed-size container, so `empty()` always returns false. I also tried using `std::pair`, but it likewise always contains two values—and it doesn't even have an `empty()` function.
created_at: 2025-05-14T01:56:05Z
author_association: NONE
diff_hunk: @@ -102,15 +102,30 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
-    commutative_inputs_enabled_ = true;
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::array<size_t, 2> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
+    commutative_inputs_ = inputs;
     return *this;
 }
 
-bool op_schema_t::get_commutative_inputs() const {
-    return commutative_inputs_enabled_;
+bool op_schema_t::is_commutative_inputs(
+        const size_t input0, const size_t input1) const {
+
+    return is_commutative_op()
+            && ((commutative_inputs_[0] == input0
+                        && commutative_inputs_[1] == input1)
+                    || (commutative_inputs_[0] == input1
+                            && commutative_inputs_[1] == input0));
+}
+
+bool op_schema_t::is_commutative_op() const {
+    return commutative_inputs_[0] != SIZE_MAX
+            && commutative_inputs_[1] != SIZE_MAX;
path: src/graph/interface/op_schema.cpp
user: login: TaoLv
body: Thanks for the explain. I would suggest to not use `SIZE_MAX` as magical numbers in this case.
If we don't consider `std::optional` which requires C++17, two options I can imagine:
1. Still use std::set as your previous solution, but check the size of the set when needed. It should be either empty or 2 elements.
2. Use a vector/set/pair/etc of std::pair. Check empty if it's not set. Once set, access the elements in the pair.
created_at: 2025-05-14T03:05:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -102,15 +102,30 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
-    commutative_inputs_enabled_ = true;
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::array<size_t, 2> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
+    commutative_inputs_ = inputs;
     return *this;
 }
 
-bool op_schema_t::get_commutative_inputs() const {
-    return commutative_inputs_enabled_;
+bool op_schema_t::is_commutative_inputs(
+        const size_t input0, const size_t input1) const {
+
+    return is_commutative_op()
+            && ((commutative_inputs_[0] == input0
+                        && commutative_inputs_[1] == input1)
+                    || (commutative_inputs_[0] == input1
+                            && commutative_inputs_[1] == input0));
+}
+
+bool op_schema_t::is_commutative_op() const {
+    return commutative_inputs_[0] != SIZE_MAX
+            && commutative_inputs_[1] != SIZE_MAX;
path: src/graph/interface/op_schema.cpp
user: login: TaoLv
body: Casting @uxlfoundation/onednn-graph  to see if other suggestions here.
created_at: 2025-05-14T03:06:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -102,15 +102,30 @@ op_schema_t &op_schema_t::set_output(
     return *this;
 }
 
-op_schema_t &op_schema_t::set_commutative_inputs() {
-    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) == 2,
-            "commutative inputs can only be enabled for ops with two inputs");
-    commutative_inputs_enabled_ = true;
+op_schema_t &op_schema_t::set_commutative_inputs(
+        const std::array<size_t, 2> &inputs) {
+    assertm(num_inputs_.size() == 1 && *(num_inputs_.begin()) > 1,
+            "The number of inputs is at least two");
+    assertm(std::all_of(inputs.begin(), inputs.end(),
+                    [&](size_t idx) { return idx < *(num_inputs_.begin()); }),
+            "each input value must be less than input numbers");
+    commutative_inputs_ = inputs;
     return *this;
 }
 
-bool op_schema_t::get_commutative_inputs() const {
-    return commutative_inputs_enabled_;
+bool op_schema_t::is_commutative_inputs(
+        const size_t input0, const size_t input1) const {
+
+    return is_commutative_op()
+            && ((commutative_inputs_[0] == input0
+                        && commutative_inputs_[1] == input1)
+                    || (commutative_inputs_[0] == input1
+                            && commutative_inputs_[1] == input0));
+}
+
+bool op_schema_t::is_commutative_op() const {
+    return commutative_inputs_[0] != SIZE_MAX
+            && commutative_inputs_[1] != SIZE_MAX;
path: src/graph/interface/op_schema.cpp
user: login: ShanSimu
body: I adopted Method 1, performing a size check on the inputs in `set_commutative_inputs`
created_at: 2025-05-14T07:29:51Z
author_association: NONE

PR comments:
user: login: ElaineBao
created_at: 2025-04-25T06:30:29Z
body: please format the commit message with correct component, graph: interface: ..., graph: utils: pm: ...
author_association: CONTRIBUTOR
user: login: ShanSimu
created_at: 2025-04-25T06:47:58Z
body: > please format the commit message with correct component, graph: interface: ..., graph: utils: pm: ...

Thanks for letting me know. I split the commit into three parts based on file paths, using the directory structure as the distinction: `graph: interface: ...`, `graph: utils: pm: ...` and `gtests: unit: ....`
author_association: NONE
user: login: ShanSimu
created_at: 2025-04-29T06:02:38Z
body: make test
set test_scope=NIGHTLY
disable benchdnn_all
enable benchdnn_graph
author_association: NONE
user: login: ShanSimu
created_at: 2025-04-30T08:00:56Z
body: make test
set test_scope=NIGHTLY
disable benchdnn_all
enable benchdnn_graph
author_association: NONE
user: login: ShanSimu
created_at: 2025-05-06T02:16:58Z
body: make test
set test_scope=NIGHTLY
disable benchdnn_all
enable benchdnn_graph
author_association: NONE
user: login: ShanSimu
created_at: 2025-05-07T08:46:45Z
body: make test
set test_scope=NIGHTLY
disable benchdnn_all
enable benchdnn_graph
author_association: NONE
user: login: ShanSimu
created_at: 2025-05-09T11:19:06Z
body: make test
set test_scope=NIGHTLY
disable benchdnn_all
enable benchdnn_graph
author_association: NONE
user: login: ShanSimu
created_at: 2025-05-15T02:41:47Z
body: make test
set test_scope=NIGHTLY
disable benchdnn_all
enable benchdnn_graph
author_association: NONE