masahi commented on a change in pull request #5231: [POC] Pattern Language, 
Matcher, and Rewriter V0
URL: https://github.com/apache/incubator-tvm/pull/5231#discussion_r409842978
 
 

 ##########
 File path: src/relay/ir/dataflow_matcher.cc
 ##########
 @@ -0,0 +1,440 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*!
+ * \file src/tvm/relay/dataflow_matcher.cc
+ * \brief The dataflow pattern matcher for Relay.
+ */
+
+#include <tvm/relay/analysis.h>
+#include <tvm/relay/dataflow_matcher.h>
+#include <tvm/relay/expr_functor.h>
+#include <tvm/relay/transform.h>
+
+namespace tvm {
+namespace relay {
+
+// Pattern Matcher
+
+
+class DominatorMatcher;
+
+class DFPatternMatcher : public DFPatternFunctor<bool(const DFPattern&, const 
Expr&)> {
+ public:
+  explicit DFPatternMatcher(const Expr& root_expr) : 
expr_graph_(CreateIndexedGraph(root_expr)) {}
+  bool Match(const DFPattern& pattern, const Expr& expr);
+
+ protected:
+  bool VisitDFPattern(const DFPattern& pattern, const Expr& expr) override;
+  bool VisitDFPattern_(const AltPatternNode* op, const Expr& expr) override;
+  bool VisitDFPattern_(const AttrPatternNode* op, const Expr& expr) override;
+  bool VisitDFPattern_(const CallPatternNode* op, const Expr& expr) override;
+  bool VisitDFPattern_(const DominatorPatternNode* op, const Expr& expr) 
override;
+  bool VisitDFPattern_(const ExprPatternNode* op, const Expr& expr) override;
+  bool VisitDFPattern_(const TupleGetItemPatternNode* op, const Expr& expr) 
override;
+  bool VisitDFPattern_(const TuplePatternNode* op, const Expr& expr) override;
+  bool VisitDFPattern_(const TypePatternNode* op, const Expr& expr) override;
+  bool VisitDFPattern_(const VarPatternNode* op, const Expr& expr) override;
+  bool VisitDFPattern_(const WildcardPatternNode* op, const Expr& expr) 
override;
+
+  void ClearMap(size_t watermark);
+  std::unordered_map<DFPattern, Expr, ObjectHash, ObjectEqual> memo_;
+  std::vector<DFPattern> matched_nodes_;
+  IndexedGraph<Expr> expr_graph_;
+  friend DominatorMatcher;
+};
+
+bool DFPatternMatcher::Match(const DFPattern& pattern, const Expr& expr) {
+  memo_.clear();
+  matched_nodes_.clear();
+  return VisitDFPattern(pattern, expr);
+}
+
+void DFPatternMatcher::ClearMap(size_t watermark) {
+  for (size_t i = watermark; i < matched_nodes_.size(); ++i) {
+    memo_.erase(matched_nodes_[i]);
+  }
+  matched_nodes_.erase(matched_nodes_.begin() + watermark, 
matched_nodes_.end());
+}
+
+bool DFPatternMatcher::VisitDFPattern(const DFPattern& pattern, const Expr& 
expr) {
+  if (memo_.count(pattern)) {
+    return expr.same_as(memo_[pattern]);
+  } else {
+    auto watermark = matched_nodes_.size();
+    auto out = DFPatternFunctor::VisitDFPattern(pattern, expr);
+    if (out) {
+      memo_[pattern] = expr;
+      matched_nodes_.push_back(pattern);
+    } else {
+      ClearMap(watermark);
+    }
+    return out;
+  }
+}
+
+bool DFPatternMatcher::VisitDFPattern_(const AltPatternNode* op, const Expr& 
expr) {
+  return VisitDFPattern(op->left, expr) || VisitDFPattern(op->right, expr);
+}
+
+bool DFPatternMatcher::VisitDFPattern_(const AttrPatternNode* attr_pattern, 
const Expr& expr) {
+  bool matches = false;
+  if (const auto* op_node = expr.as<OpNode>()) {
+    Op op = GetRef<Op>(op_node);
+    auto attributes = attr_pattern->attrs.as<DictAttrsNode>()->dict;
+    for (auto kv : attributes) {
+      auto attr_name = kv.first;
+      auto attr_value = kv.second;
+      auto op_map = Op::GetAttr<TVMRetValue>(attr_name);
+      if (op_map.count(op)) {
+        switch (op_map[op].type_code()) {
+          case kDLInt:
+            if (auto* val = kv.second.as<IntImmNode>()) {
+              matches = val->value == op_map[op].operator int64_t();
+            }
+            break;
+          case kDLFloat:
+            if (auto* val = kv.second.as<FloatImmNode>()) {
+              matches = val->value == op_map[op].operator double();
+            }
+            break;
+          case kTVMStr:
+            if (auto* val = kv.second.as<tir::StringImmNode>()) {
+              matches = val->value == op_map[op].operator std::string();
+            }
+            break;
+          default:
+            throw "Unsupported type";
+        }
+      }
+    }
+  }
+  return matches;
+}
+
+Array<DFPattern> reverse(const Array<DFPattern>& args) {
+  Array<DFPattern> new_args;
+  for (auto it = args.rbegin(); it != args.rend(); ++it) {
+    new_args.push_back(*it);
+  }
+  return new_args;
+}
+
+bool DFPatternMatcher::VisitDFPattern_(const CallPatternNode* op, const Expr& 
expr) {
+  // utilities
+  auto get_op_node = [](const CallPatternNode* op) -> const tvm::OpNode* {
+    if (op) {
+      if (auto* expr_pattern = op->op.as<ExprPatternNode>()) {
+        return expr_pattern->expr.as<OpNode>();
+      }
+    }
+    return nullptr;
+  };
+  auto is_pattern_op = [&get_op_node](const CallPatternNode* op, std::string 
op_type) {
+    if (const auto* op_node = get_op_node(op)) {
+      if (op_node->name == op_type) {
+        return true;
+      }
+    }
+    return false;
+  };
+
+  auto is_expr_op = [](const Expr& expr, std::string op_type) {
+    if (const auto* call_node = expr.as<CallNode>()) {
+      if (const auto* op_node = call_node->op.as<OpNode>()) {
+        if (op_node->name == op_type) {
+          return true;
+        }
+      }
+    }
+    return false;
+  };
+  // logic
+  auto watermark = matched_nodes_.size();
+  if (const auto* call_node = expr.as<CallNode>()) {
+    auto matches_op = VisitDFPattern(op->op, call_node->op);
+    if (matches_op) {
+      auto watermark2 = matched_nodes_.size();
+
+      auto match_args = [this, &watermark2](const Array<DFPattern> 
pattern_args,
+                                            const Array<Expr> expr_args) {
+        bool matches = true;
+        size_t i = 0;
+        if (pattern_args.size() == expr_args.size()) {
+          while (matches && i < pattern_args.size()) {
+            matches &= VisitDFPattern(pattern_args[i], expr_args[i]);
+            ++i;
+          }
+        } else {
+          matches = false;
+        }
+        if (!matches) {
+          ClearMap(watermark2);
+        }
+        return matches;
+      };
+
+      // Standard case
+      if (match_args(op->args, call_node->args)) {
+        return true;
+      }
+      // Commutative Matching
+      if (const OpNode* op_node = get_op_node(op)) {
+        if ((op_node->name == "add") || (op_node->name == "multiply")) {
+          if (match_args(reverse(op->args), call_node->args)) {
+            return true;
+          }
+        }
+      }
+    } else {
+      ClearMap(watermark);
+      // associate divide/multiply
+      if (is_pattern_op(op, "divide")) {
+        if (const auto* arg_node = op->args[0].as<CallPatternNode>()) {
+          if (is_pattern_op(arg_node, "multiply")) {
+            if (is_expr_op(expr, "multiply")) {
+              if (is_expr_op(call_node->args[0], "divide") ||
+                  is_expr_op(call_node->args[1], "divide")) {
+                bool out = false;
+                for (size_t arg_id = 0; arg_id < 2; ++arg_id) {
+                  auto div = CallPatternNode::make(op->op, 
{arg_node->args[arg_id], op->args[1]},
+                                                   op->attrs, op->type_args);
+                  auto mul =
+                      CallPatternNode::make(arg_node->op, 
{arg_node->args[(arg_id + 1) % 2], div},
+                                            arg_node->attrs, 
arg_node->type_args);
+                  out = VisitDFPattern(mul, expr);
+                  if (out) {
+                    return out;
+                  } else {
+                    ClearMap(watermark);
+                  }
+                }
+                return out;
+              }
+            }
+          }
+        }
+      }
+      if (is_pattern_op(op, "multiply")) {
+        // associate multiply/divide
+        for (size_t arg_id = 0; arg_id < 2; ++arg_id) {
+          if (auto* arg_node = op->args[arg_id].as<CallPatternNode>()) {
+            if (is_pattern_op(arg_node, "divide")) {
+              if (is_expr_op(expr, "divide")) {
+                if (is_expr_op(call_node->args[0], "multiply") ||
+                    is_expr_op(call_node->args[1], "multiply")) {
+                  auto mul =
+                      CallPatternNode::make(op->op, {arg_node->args[0], 
op->args[(arg_id + 1) % 2]},
+                                            op->attrs, op->type_args);
+                  auto div = CallPatternNode::make(arg_node->op, {mul, 
arg_node->args[1]},
+                                                   arg_node->attrs, 
arg_node->type_args);
+                  return VisitDFPattern(div, expr);
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  return false;
+}
+
+// Friend class to do recursive dominator matching
+class DominatorMatcher {
+ public:
+  DominatorMatcher(DFPatternMatcher* matcher, const DominatorPatternNode* op, 
const Expr& expr)
+      : matcher_(matcher), op_(op), expr_(expr) {
+    watermark_ = matcher_->matched_nodes_.size();
+    pattern_graph_ = CreateIndexedGraph(GetRef<DFPattern>(op));
+  }
+  bool Match() {
+    if (matcher_->VisitDFPattern(op_->child, expr_)) {
+      auto dominated_exprs = FindDominated(op_->child);
+      matcher_->ClearMap(watermark_);
+
+      bool matches = FindParent(expr_, dominated_exprs);
+      if (matches) {
+        matcher_->ClearMap(watermark_);
+        matcher_->memo_[op_->child] = expr_;
+        matcher_->matched_nodes_.push_back(op_->child);
+      }
+      return matches;
+    }
+    return false;
+  }
+
+ protected:
+  DFPatternMatcher* matcher_;
+  const DominatorPatternNode* op_;
+  IndexedGraph<DFPattern> pattern_graph_;
+  Expr expr_;
+  size_t watermark_;
 
 Review comment:
   ok if you don't want to add `FindParent` in the `DFPatternMatcher`, I don't 
have preference over the friend class or a recursive lambda solution.
   
   I can see the value of decoupling the dominator matching logic from the main 
matcher, but since `DFPatternMatcher` is a private class in this .cc, I think 
it is fine to put `FindParent` there as an additional impl detail to handle 
dominator patterns. `FindDominated` can be lifted to a free function if you 
pass `memo_`, so it doesn't need to be a part of `DFPatternMatcher`. 
   
   Adding `FindParent` (perhaps with a better name) as a private member 
function would be definitely lighter-weight and less controversial change than 
introducing friend class. But this is not a strong opinion, I think decoupling 
of `DominatorMatcher`, which requires more complicated logic, at the cost of a 
friend is reasonable.
   
   

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to