This is an automated email from the ASF dual-hosted git repository.

tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new 08db12de5f [REFACTOR][SYM] Retire optional simplifier extensions 
(#20712)
08db12de5f is described below

commit 08db12de5f5dc34ae03a50b9e8a580b236459579
Author: Tianqi Chen <[email protected]>
AuthorDate: Wed Oct 14 07:07:14 2026 -0400

    [REFACTOR][SYM] Retire optional simplifier extensions (#20712)
---
 include/tvm/sym/analyzer.h                         |  112 +--
 include/tvm/tirx/type.h                            |    6 +-
 python/tvm/relax/transform/__init__.py             |    1 -
 python/tvm/relax/transform/transform.py            |   17 -
 python/tvm/sym/__init__.py                         |    1 -
 python/tvm/sym/analyzer.py                         |   44 +-
 src/ir/expr.cc                                     |    3 +-
 src/relax/ir/dataflow_matcher.cc                   |   65 +-
 src/relax/ir/dataflow_matcher.h                    |    6 +-
 src/relax/transform/adjust_matmul_order.cc         |  356 -------
 src/s_tir/transform/stmt_simplify.cc               |    1 -
 src/sym/analyzer.cc                                |   24 +-
 src/sym/conjunctive_normal_form.cc                 |  445 ---------
 src/sym/conjunctive_normal_form.h                  |   50 -
 ...on_analyzer.cc => known_comparison_analyzer.cc} |  392 +-------
 src/sym/rewrite_simplify.cc                        |  264 +----
 src/sym/rewrite_simplify.h                         |   19 -
 src/tirx/transform/stmt_simplify.cc                |    1 -
 .../relax/test_transform_adjust_matmul_order.py    | 1034 --------------------
 tests/python/sym/test_sym_analyzer_object.py       |   26 +-
 tests/python/sym/test_sym_rewrite_simplify.py      |   36 -
 .../tirx-transform/test_tir_transform_simplify.py  |   25 -
 22 files changed, 132 insertions(+), 2796 deletions(-)

diff --git a/include/tvm/sym/analyzer.h b/include/tvm/sym/analyzer.h
index c0d4988f9a..ce90fcbb20 100644
--- a/include/tvm/sym/analyzer.h
+++ b/include/tvm/sym/analyzer.h
@@ -306,90 +306,6 @@ class RewriteSimplifier {
   TVM_TIRX_DLL std::function<void()> EnterConstraint(const PrimExpr& 
constraint,
                                                      bool is_assume = false);
 
-  /*! \brief Flags to enable more computationally-intensive simplifications
-   *
-   * These simplifications may be required for specific schedules, but
-   * would impose too high a compile-time cost to enable by default.
-   * They can be enabled on an as-needed basis by calling
-   * `RewriteSimplifier::SetEnabledExtensions` prior to using
-   * `RewriteSimplifier::operator()`.
-   *
-   * Flags are defined as powers of two to allow future expansion.  To
-   * enable multiple extensions, a user should pass a bitwise OR of the
-   * flags for each desired extension.
-   */
-  enum Extension {
-    // No extensions enabled
-    kNone = 0,
-
-    /* When simplifying an inequality, attempt to use scope-based knowns.
-     *
-     * Example:
-     * if_then_else(i<j && j<k, i<k, false) => if_then_else(i<j && j<k, true, 
false)
-     */
-    kTransitivelyProveInequalities = (1 << 0),
-
-    /* When simplifying a boolean expression, convert to an AND of ORs
-     * (conjunctive normal form).
-     *
-     * Example:
-     *   (a && b) || c => (a || c) && (b || c)
-     */
-    kConvertBooleanToAndOfOrs = (1 << 1),
-
-    /* When simplifying a boolean AND or a boolean OR, simplify each
-     * branch under the assumption that the other branch does not
-     * already dominate the result.  That is, simplify each branch of
-     * (A && B) under the assumption that the other branch is true,
-     * and simplify each branch of (A || B) under the assumption that
-     * the other branch is false.
-     *
-     * Example:
-     *   (n < 10) && (n < 5) => (n < 10)
-     *   (n < 10) || (n < 5) => (n < 5)
-     */
-    kApplyConstraintsToBooleanBranches = (1 << 2),
-
-    /* Special handling for expressions `(A+B)*C < (A*B)*D`
-     *
-     * Expressions of the form `(A+B)*C < (A*B)*D` can occur occur
-     * when comparing the number of operations required for two
-     * different orderings in which matrix multiplications can be
-     * performed.  Proving or disproving this conditional allows an
-     * optimal order of execution to be selected, even for dynamic
-     * argument shapes.
-     *
-     * The default behavior of `ConstIntBounds` assumes that each term
-     * in an expression is independent, and is insufficient to prove
-     * these inequalities.  For example, the maximum value of `(A+B)*C
-     * - (A*B)*D` is determined by taking the maximum value of
-     * `(A+B)*C` and subtracting the minimum value of `(A*B)*D`.
-     * While this algorithm can be applied in all cases, the bound it
-     * provides is looser than strictly required.
-     *
-     * This extension adds a check for this case.  When `A`, `B`, `C`,
-     * and `D` are all positive values, as is the case for tensor
-     * shapes, the inequality can be written as `1/A + 1/B < D/C`.  If
-     * this inequality holds for the minimum values of `A`, `B`, and
-     * `D`, along with the maximum value of `C`, then the inequality
-     * holds for all values.
-     *
-     * This extension requires little to no performance overhead, and
-     * may be enabled by default in future releases.
-     */
-    kComparisonOfProductAndSum = (1 << 3),
-  };
-
-  /*! \brief Enable an optional extension or extensions
-   *
-   * \param flags A bitwise OR of all optional extensions that should
-   * be enabled.
-   */
-  TVM_TIRX_DLL void SetEnabledExtensions(Extension flags);
-
-  /*! \brief Return the currently enabled extensions */
-  TVM_TIRX_DLL Extension GetEnabledExtensions() const;
-
   /*! \brief Return the statistics counters */
   TVM_TIRX_DLL ffi::ObjectRef GetStatsCounters() const;
 
@@ -482,10 +398,9 @@ inline constexpr CompareResult operator|(CompareResult 
lhs, CompareResult rhs) {
 /*!
  * \brief Using previously specified knowns, compare the expressions provided
  *
- * Given known expressions [(a OP b), (b OP c), ..., (y OP z)], search
- * for a known result for `(a OP z)`.
+ * Compare directly related expressions, allowing constant offsets.
  */
-class TransitiveComparisonAnalyzer {
+class KnownComparisonAnalyzer {
  public:
   /* \brief Using previously specified knowns, compare the expressions provided
    *
@@ -493,19 +408,12 @@ class TransitiveComparisonAnalyzer {
    *
    * \param rhs The right-hand side of the comparison
    *
-   * \param propagate_inequalities If true, attempt to find a sequence
-   * of transitive inequalities that allow the lhs and rhs to be
-   * compared.  If false, only use the known comparison that have been
-   * directly provided.  Using `propagate_inequalities = false` is
-   * roughly equivalent to comparing against all known inequality
-   * expressions using `prim::ExprDeepEqual`, but also allows for constant
-   * offsets on either side of the inequality.
+   * Directly known comparisons also allow constant offsets on either side.
    *
    * \return The most specific result that can be proven about the
    * comparison.  If nothing can be proven, returns kUnknown.
    */
-  TVM_TIRX_DLL CompareResult TryCompare(const PrimExpr& lhs, const PrimExpr& 
rhs,
-                                        bool propagate_inequalities = true);
+  TVM_TIRX_DLL CompareResult TryCompare(const PrimExpr& lhs, const PrimExpr& 
rhs);
 
   /*! \brief Bind a variable as being equal to a known expression
    *
@@ -534,9 +442,9 @@ class TransitiveComparisonAnalyzer {
  private:
   friend class AnalyzerObj;
   friend class ConstraintContext;
-  TransitiveComparisonAnalyzer();
-  TVM_TIRX_DLL ~TransitiveComparisonAnalyzer();
-  void CopyFrom(const TransitiveComparisonAnalyzer& other);
+  KnownComparisonAnalyzer();
+  TVM_TIRX_DLL ~KnownComparisonAnalyzer();
+  void CopyFrom(const KnownComparisonAnalyzer& other);
   class Impl;
   /*! \brief Internal impl */
   std::unique_ptr<Impl> impl_;
@@ -725,8 +633,8 @@ class TVM_TIRX_DLL AnalyzerObj : public ffi::Object {
   CanonicalSimplifier canonical_simplify;
   /*! \brief sub-analyzer: int set */
   IntSetAnalyzer int_set;
-  /*! \brief sub-analyzer transitive comparisons */
-  TransitiveComparisonAnalyzer transitive_comparisons;
+  /*! \brief sub-analyzer known comparisons */
+  KnownComparisonAnalyzer known_comparisons;
   /*! \brief sub-analyzer using Z3 */
   Z3Prover z3_prover;
   /*! \brief constructor */
@@ -873,7 +781,7 @@ class TVM_TIRX_DLL AnalyzerObj : public ffi::Object {
    *
    * The returned analyzer carries the same accumulated facts (variable
    * bounds, modular sets, rewrite/canonical bindings, integer-set domains,
-   * literal constraints and transitive comparisons) as this one, but owns
+   * literal constraints and known comparisons) as this one, but owns
    * its own state: binding or simplifying on either analyzer afterwards does
    * not affect the other. This is the deep copy that handle-copying an
    * Analyzer does not provide.
diff --git a/include/tvm/tirx/type.h b/include/tvm/tirx/type.h
index 05f3844821..7fc164e82f 100644
--- a/include/tvm/tirx/type.h
+++ b/include/tvm/tirx/type.h
@@ -93,8 +93,7 @@ class TensorTypeNode : public TypeNode {
     refl::ObjectDef<TensorTypeNode>()
         .def_ro("shape", &TensorTypeNode::shape, 
refl::AttachFieldFlag::SEqHashDefSimple())
         .def_ro("dtype", &TensorTypeNode::dtype)
-        .def_ro("scope", &TensorTypeNode::scope,
-                refl::DefaultValue(runtime::MemoryScope::Global()))
+        .def_ro("scope", &TensorTypeNode::scope, 
refl::DefaultValue(runtime::MemoryScope::Global()))
         .def_ro("strides", &TensorTypeNode::strides, 
refl::AttachFieldFlag::SEqHashDefSimple(),
                 refl::DefaultValue(ffi::Array<PrimExpr>()))
         .def_ro("elem_offset", &TensorTypeNode::elem_offset,
@@ -140,8 +139,7 @@ class TensorTypeNode : public TypeNode {
 class TensorType : public Type {
  public:
   TVM_TIRX_DLL TensorType(ffi::Array<PrimExpr> shape, PrimType dtype = 
PrimType::Float(32),
-                          runtime::MemoryScope scope = {},
-                          ffi::Array<PrimExpr> strides = {},
+                          runtime::MemoryScope scope = {}, 
ffi::Array<PrimExpr> strides = {},
                           ffi::Optional<PrimExpr> elem_offset = std::nullopt,
                           int data_alignment = 0, int offset_factor = 0,
                           ffi::Optional<Layout> layout = std::nullopt, 
Location loc = UnknownLoc());
diff --git a/python/tvm/relax/transform/__init__.py 
b/python/tvm/relax/transform/__init__.py
index e12604b20f..88ce17d982 100644
--- a/python/tvm/relax/transform/__init__.py
+++ b/python/tvm/relax/transform/__init__.py
@@ -18,7 +18,6 @@
 """Relax transformations."""
 
 from .transform import (
-    AdjustMatmulOrder,
     AllocateWorkspace,
     AlterOpImpl,
     AnnotateTIROpPattern,
diff --git a/python/tvm/relax/transform/transform.py 
b/python/tvm/relax/transform/transform.py
index abc6af1876..07e66d0342 100644
--- a/python/tvm/relax/transform/transform.py
+++ b/python/tvm/relax/transform/transform.py
@@ -1450,23 +1450,6 @@ def UpdateParamType(ty_func: Callable[[Var], Type | 
None]):
     return _ffi_api.UpdateParamType(ty_func)  # type: ignore
 
 
-def AdjustMatmulOrder():
-    """Reorder `x*(A*B)` to `(x*A)*B`
-
-    Useful for optimizing LoRA computations, where `matmul(x,
-    LoraA*LoraB)` may be computed as `matmul(matmul(x, LoraA),
-    LoraB)`, reducing the total memory usage.
-
-
-    Returns
-    -------
-    ret : tvm.ir.transform.Pass
-        The corresponding pass.
-    """
-
-    return _ffi_api.AdjustMatmulOrder()  # type: ignore
-
-
 def ExpandMatmulOfSum():
     """Expand `matmul(x, A+B)` to `matmul(x,A) + matmul(x,B)`
 
diff --git a/python/tvm/sym/__init__.py b/python/tvm/sym/__init__.py
index 3a3e091eff..59274c093e 100644
--- a/python/tvm/sym/__init__.py
+++ b/python/tvm/sym/__init__.py
@@ -29,7 +29,6 @@ from .analyzer import (
     ConstIntBound,
     Analyzer,
     ProofStrength,
-    Extension,
     CompareResult,
     allow_uint_as_index,
 )
diff --git a/python/tvm/sym/analyzer.py b/python/tvm/sym/analyzer.py
index 560b45a364..ce34101cf0 100644
--- a/python/tvm/sym/analyzer.py
+++ b/python/tvm/sym/analyzer.py
@@ -52,19 +52,6 @@ class CompareResult(enum.IntEnum):
     UNKNOWN = 7
 
 
-class Extension(enum.Flag):
-    """Extensions enabled for RewriteSimplifier
-
-    Values should match `RewriteSimplifier::Extensions`
-    """
-
-    NoExtensions = 0
-    TransitivelyProveInequalities = 1 << 0
-    ConvertBooleanToAndOfOrs = 1 << 1
-    ApplyConstraintsToBooleanBranches = 1 << 2
-    ComparisonOfProductAndSum = 1 << 3
-
-
 @tvm_ffi.register_object("sym.ModularSet")
 class ModularSet(Object):
     """Represent range of (coeff * x + base) for x in Z"""
@@ -485,10 +472,8 @@ class Analyzer(Object):
         """
         return _ffi_api.AnalyzerCanProveEqual(self, lhs, rhs)
 
-    def try_compare(
-        self, lhs: ir.Expr, rhs: ir.Expr, propagate_inequalities: bool = True
-    ) -> CompareResult:
-        """Compare lhs and rhs using previously provided known comparisons.
+    def try_compare(self, lhs: ir.Expr, rhs: ir.Expr) -> CompareResult:
+        """Compare lhs and rhs using directly known comparisons and constant 
offsets.
 
         Parameters
         ----------
@@ -498,36 +483,13 @@ class Analyzer(Object):
         rhs : Expr
             The right-hand side of the comparison.
 
-        propagate_inequalities : bool
-            If true, attempt to find a sequence of transitive inequalities that
-            allow lhs and rhs to be compared.
-
         Returns
         -------
         result : CompareResult
             The most specific result that can be proven about the comparison.
             Returns ``CompareResult.UNKNOWN`` when nothing can be proven.
         """
-        return CompareResult(_ffi_api.AnalyzerTryCompare(self, lhs, rhs, 
propagate_inequalities))
-
-    @property
-    def enabled_extensions(self) -> Extension:
-        """Return the currently enabled extensions"""
-        value = _ffi_api.AnalyzerGetEnabledExtensions(self)
-        return Extension(value)
-
-    @enabled_extensions.setter
-    def enabled_extensions(self, flags: int | Extension):
-        """Enable extensions for the analyzer
-
-        Parameters
-        ----------
-        flags: Union[int,Extension]
-
-            The extensions to enable.
-        """
-        flags = Extension(flags).value
-        _ffi_api.AnalyzerSetEnabledExtensions(self, flags)
+        return CompareResult(_ffi_api.AnalyzerTryCompare(self, lhs, rhs))
 
 
 @contextmanager
diff --git a/src/ir/expr.cc b/src/ir/expr.cc
index 59ec239ee2..81b93398ff 100644
--- a/src/ir/expr.cc
+++ b/src/ir/expr.cc
@@ -718,7 +718,8 @@ TensorLoad::TensorLoad(Expr source, ffi::Array<PrimExpr> 
indices, Location loc)
 
 TensorLoad TensorLoad::Unchecked(PrimType ty, Expr source, 
ffi::Array<PrimExpr> indices,
                                  Location loc) {
-  TVM_FFI_CHECK(ty.defined() && ty.as<PrimTypeNode>(), TypeError) << 
"TensorLoad requires a concrete PrimType";
+  TVM_FFI_CHECK(ty.defined() && ty.as<PrimTypeNode>(), TypeError)
+      << "TensorLoad requires a concrete PrimType";
   TVM_FFI_CHECK(source.defined(), TypeError) << "TensorLoad requires a defined 
source";
   auto node = ffi::make_object<TensorLoadNode>(std::move(source));
   node->ty = std::move(ty);
diff --git a/src/relax/ir/dataflow_matcher.cc b/src/relax/ir/dataflow_matcher.cc
index 93d5d09450..ce5f59ab4c 100644
--- a/src/relax/ir/dataflow_matcher.cc
+++ b/src/relax/ir/dataflow_matcher.cc
@@ -26,6 +26,7 @@
 
 #include <tvm/ffi/cast.h>
 #include <tvm/ffi/extra/structural_equal.h>
+#include <tvm/ir/analysis.h>
 #include <tvm/ir/prim/op.h>
 #include <tvm/relax/analysis.h>
 #include <tvm/relax/dataflow_matcher.h>
@@ -36,6 +37,7 @@
 #include <tvm/runtime/logging.h>
 #include <tvm/sym/analyzer.h>
 
+#include <algorithm>
 #include <array>
 #include <cstddef>
 #include <limits>
@@ -428,6 +430,67 @@ bool DFPatternMatcher::VisitDFPattern_(const 
UnorderedTuplePatternNode* op, cons
   return false;
 }
 
+// Check accumulated shape equalities without changing the constraints 
retained for backtracking.
+// Bindings live only in this check, and use variable identity rather than 
printed names.
+static bool ShapeConstraintsConsistent(const PrimExpr& condition) {
+  std::vector<PrimExpr> constraints{condition};
+  for (size_t i = 0; i < constraints.size();) {
+    if (const auto* conjunction = constraints[i].as<prim::AndNode>()) {
+      PrimExpr lhs = conjunction->a;
+      PrimExpr rhs = conjunction->b;
+      constraints[i] = std::move(lhs);
+      constraints.push_back(std::move(rhs));
+    } else {
+      ++i;
+    }
+  }
+
+  Analyzer analyzer;
+  // Follow every possible local alias before binding, including chains longer 
than
+  // the analyzer's default two simplification passes.
+  int simplify_steps = 2 * static_cast<int>(constraints.size());
+  // Each successful binding eliminates a variable from an equality. Revisit 
earlier
+  // equalities that may become directly bindable after a later constraint is 
processed.
+  for (size_t iteration = 0; iteration < constraints.size(); ++iteration) {
+    bool changed = false;
+    for (const PrimExpr& constraint : constraints) {
+      PrimExpr simplified = analyzer->Simplify(constraint, simplify_steps);
+      if (prim::IsZero(simplified)) return false;
+      if (const auto* equality = simplified.as<prim::EQNode>()) {
+        PrimExpr lhs = equality->a;
+        PrimExpr rhs = equality->b;
+        if (!lhs.as<tvm::VarNode>()) std::swap(lhs, rhs);
+        if (const auto* var = lhs.as<tvm::VarNode>();
+            var && SideEffect(rhs) <= CallEffectKind::kPure) {
+          auto variables = tvm::UndefinedVars(rhs);
+          if (std::none_of(variables.begin(), variables.end(),
+                           [var](const tvm::Var& used) { return used.get() == 
var; })) {
+            analyzer->Bind(ffi::GetRef<tvm::Var>(var), rhs);
+            changed = true;
+          }
+        }
+      }
+    }
+    if (!changed) break;
+  }
+  // Composite dimensions may not admit a variable binding. Check each 
remaining
+  // equality against its peers, so repeated expressions also detect 
contradictions.
+  for (PrimExpr& constraint : constraints) {
+    constraint = analyzer->Simplify(constraint, simplify_steps);
+    if (prim::IsZero(constraint)) return false;
+  }
+  for (size_t i = 0; i < constraints.size(); ++i) {
+    if (prim::IsOne(constraints[i])) continue;
+    PrimExpr others = IntImm::Bool(true);
+    for (size_t j = 0; j < constraints.size(); ++j) {
+      if (i != j) others = others && constraints[j];
+    }
+    With<sym::ConstraintContext> scope(analyzer, others);
+    if (prim::IsZero(analyzer->Simplify(constraints[i], simplify_steps))) 
return false;
+  }
+  return true;
+}
+
 bool DFPatternMatcher::VisitDFPattern_(const TypePatternNode* op, const Expr& 
expr0) {
   if (!VisitDFPattern(op->pattern, expr0)) {
     return false;
@@ -446,7 +509,7 @@ bool DFPatternMatcher::VisitDFPattern_(const 
TypePatternNode* op, const Expr& ex
   if (auto* as_int = symbolic_expr_condition_.as<IntImmNode>()) {
     return static_cast<bool>(as_int->value);
   } else {
-    return true;
+    return ShapeConstraintsConsistent(symbolic_expr_condition_);
   }
 }
 
diff --git a/src/relax/ir/dataflow_matcher.h b/src/relax/ir/dataflow_matcher.h
index 677b5526b8..98a4adab74 100644
--- a/src/relax/ir/dataflow_matcher.h
+++ b/src/relax/ir/dataflow_matcher.h
@@ -42,11 +42,7 @@ class DFPatternMatcher : public DFPatternFunctor<bool(const 
DFPattern&, const Ex
   using var2val_t = ffi::Map<Var, Expr>;
 
   explicit DFPatternMatcher() : DFPatternMatcher(var2val_t{}) {}
-  explicit DFPatternMatcher(var2val_t var2val) : var2val_(std::move(var2val)) {
-    // Propagate shape constraints across conjunctions regardless of operand 
order.
-    analyzer_->rewrite_simplify.SetEnabledExtensions(
-        sym::RewriteSimplifier::kApplyConstraintsToBooleanBranches);
-  }
+  explicit DFPatternMatcher(var2val_t var2val) : var2val_(std::move(var2val)) 
{}
   bool Match(const DFPattern& pattern, const Expr& expr);
   ffi::Map<DFPattern, Expr> GetMemo() { return memo_; }
 
diff --git a/src/relax/transform/adjust_matmul_order.cc 
b/src/relax/transform/adjust_matmul_order.cc
deleted file mode 100644
index 56d2346833..0000000000
--- a/src/relax/transform/adjust_matmul_order.cc
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * 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 tvm/relax/transform/adjust_matmul_order.cc
- * \brief Re-order `matmul(matmul(A,B), x)` to `matmul(A, matmul(B,x))`
- */
-
-#include <tvm/ffi/reflection/registry.h>
-#include <tvm/ir/prim/op.h>
-#include <tvm/relax/analysis.h>
-#include <tvm/relax/dataflow_matcher.h>
-#include <tvm/relax/expr.h>
-#include <tvm/relax/expr_functor.h>
-#include <tvm/relax/op/linear_algebra.h>
-#include <tvm/relax/op/manipulate.h>
-#include <tvm/relax/transform.h>
-
-#include <optional>
-#include <unordered_set>
-#include <vector>
-
-#include "../op/op_common.h"
-#include "../op/tensor/linear_algebra.h"
-#include "../op/tensor/manipulate.h"
-
-namespace tvm {
-namespace relax {
-using namespace tvm::prim;
-
-namespace {
-
-ffi::Array<PrimExpr> GetBatchPrefix(const ffi::Array<PrimExpr>& shape) {
-  if (shape.size() <= 2) return {};
-  return {shape.begin(), shape.end() - 2};
-}
-
-PrimExpr ProductDims(const ffi::Array<PrimExpr>& dims) {
-  PrimExpr product = IntImm::Int64(1);
-  for (const auto& dim : dims) product = product * dim;
-  return product;
-}
-
-bool IsLastTwoDimsSwap(const Expr& expr) {
-  const auto* call = expr.as<CallNode>();
-  if (call == nullptr) return false;
-
-  const auto* attrs = call->attrs.as<PermuteDimsAttrs>();
-  const auto* input_type = GetTypeAs<TensorTypeNode>(call->args[0]);
-  if (attrs == nullptr || input_type == nullptr || input_type->ndim < 2) 
return false;
-
-  size_t ndim = input_type->ndim;
-  if (!attrs->axes.has_value()) return ndim == 2;
-
-  const auto& axes = attrs->axes.value();
-  if (axes.size() != ndim) return false;
-  for (size_t i = 0; i < axes.size(); ++i) {
-    int64_t axis = axes[i];
-    if (axis < 0) axis += ndim;
-    size_t expected = i;
-    if (i == ndim - 2) {
-      expected = ndim - 1;
-    } else if (i == ndim - 1) {
-      expected = ndim - 2;
-    }
-    if (axis != static_cast<int64_t>(expected)) return false;
-  }
-  return true;
-}
-
-ffi::Optional<ffi::Array<PrimExpr>> InferBatchedMatmulBroadcastPrefix(
-    sym::AnalyzerObj* analyzer, const ffi::Array<PrimExpr>& x1, const 
ffi::Array<PrimExpr>& x2) {
-  auto infer_result = InferBinaryBroadcastShape(analyzer, x1, x2);
-  if (infer_result.status == 
BinaryBroadcastShapeInferResult::Status::kSuccess) {
-    return infer_result.shape;
-  }
-  return std::nullopt;
-}
-
-std::tuple<DFPattern, ffi::TypedFunction<Expr(Expr, ffi::Map<DFPattern, 
Expr>)>> CreatePatterns(
-    const Function& func) {
-  auto compile_time_arr = ComputableAtCompileTime(func);
-  std::unordered_set<Var> compile_time_lookup(compile_time_arr.begin(), 
compile_time_arr.end());
-
-  ffi::TypedFunction<bool(Expr)> is_compile_time = [compile_time_lookup](Expr 
arg) -> bool {
-    if (auto as_var = arg.as<Var>()) {
-      return compile_time_lookup.count(as_var.value());
-    } else {
-      return false;
-    }
-  };
-  ffi::TypedFunction<bool(Expr)> is_runtime = [is_compile_time](Expr arg) -> 
bool {
-    return !is_compile_time(arg);
-  };
-
-  DFPattern pat_a = WildcardPattern();
-  DFPattern pat_b = WildcardPattern();
-  DFPattern pat_c = WildcardPattern();
-
-  auto pat_matmul = IsOp("relax.matmul");
-  auto pat_permute_dims = IsOp("relax.permute_dims");
-
-  auto pat_matmul_on_lhs = pat_matmul(pat_matmul(pat_a, pat_b), pat_c);
-  auto pat_matmul_on_rhs = pat_matmul(pat_a, pat_matmul(pat_b, pat_c));
-
-  auto pat_permuted_inner_matmul_on_lhs = pat_permute_dims(pat_matmul(pat_b, 
pat_a));
-  auto pat_permuted_inner_matmul_on_rhs = pat_permute_dims(pat_matmul(pat_c, 
pat_b));
-  auto pat_permuted_matmul_on_lhs = 
pat_matmul(pat_permuted_inner_matmul_on_lhs, pat_c);
-  auto pat_permuted_matmul_on_rhs = pat_matmul(pat_a, 
pat_permuted_inner_matmul_on_rhs);
-
-  auto pat = pat_matmul_on_lhs | pat_matmul_on_rhs | 
pat_permuted_matmul_on_lhs |
-             pat_permuted_matmul_on_rhs;
-
-  PrimExpr symbolic_var_constraints = IntImm::Bool(true);
-  auto upper_bounds =
-      func->GetAttr<ffi::Map<ffi::String, 
Any>>(tvm::relax::attr::kTirVarUpperBound);
-  auto lower_bounds =
-      func->GetAttr<ffi::Map<ffi::String, 
Any>>(tvm::relax::attr::kTirVarLowerBound);
-
-  if (upper_bounds || lower_bounds) {
-    ffi::Map<ffi::String, tvm::Var> name_lookup;
-    for (const auto& tir_var : TIRVarsInType(GetType(func))) {
-      name_lookup.Set(tir_var->name, tir_var);
-      symbolic_var_constraints = symbolic_var_constraints && (0 <= 
tir_var.as_or_throw<PrimExpr>());
-    }
-
-    // Add lower bound constraints
-    if (lower_bounds) {
-      for (const auto& [key, obj_bound] : lower_bounds.value()) {
-        auto tir_var_name = key;
-        if (auto opt_var = name_lookup.Get(tir_var_name)) {
-          auto var = opt_var.value();
-          auto expr_bound = obj_bound.cast<PrimExpr>();
-          symbolic_var_constraints =
-              symbolic_var_constraints && (expr_bound <= 
var.as_or_throw<PrimExpr>());
-        }
-      }
-    }
-
-    // Add upper bound constraints
-    if (upper_bounds) {
-      for (const auto& [key, obj_bound] : upper_bounds.value()) {
-        auto tir_var_name = key;
-        if (auto opt_var = name_lookup.Get(tir_var_name)) {
-          auto var = opt_var.value();
-          auto expr_bound = obj_bound.cast<PrimExpr>();
-          symbolic_var_constraints =
-              symbolic_var_constraints && (var.as_or_throw<PrimExpr>() < 
expr_bound);
-        }
-      }
-    }
-  }
-
-  auto rewriter = [=](Expr expr, ffi::Map<DFPattern, Expr> matches) -> Expr {
-    auto expr_a = matches[pat_a];
-    auto expr_b = matches[pat_b];
-    auto expr_c = matches[pat_c];
-    auto out_dtype = expr.as<CallNode>()->attrs.as<MatmulAttrs>()->out_dtype;
-
-    // If all three components are compile-time, the order doesn't
-    // matter as the entire expression can be lifted out and
-    // pre-computed.
-    if (is_compile_time(expr_a) && is_compile_time(expr_b) && 
is_compile_time(expr_c)) {
-      return expr;
-    }
-
-    auto get_shape = [](Expr expr) -> ffi::Optional<ffi::Array<PrimExpr>> {
-      auto ty = expr->ty.as<TensorTypeNode>();
-      if (ty) {
-        return ty->GetShape();
-      } else {
-        return std::nullopt;
-      }
-    };
-
-    auto opt_shape_a = get_shape(expr_a);
-    if (!opt_shape_a) return expr;
-    auto opt_shape_b = get_shape(expr_b);
-    if (!opt_shape_b) return expr;
-    auto opt_shape_c = get_shape(expr_c);
-    if (!opt_shape_c) return expr;
-
-    auto shape_a = opt_shape_a.value();
-    auto shape_b = opt_shape_b.value();
-    auto shape_c = opt_shape_c.value();
-
-    auto permute_last_two_dims = [&](Expr expr) -> Expr {
-      auto opt_shape = get_shape(expr);
-      if (!opt_shape) return expr;
-
-      size_t ndim = opt_shape.value().size();
-      TVM_FFI_ICHECK_GE(ndim, 2);
-
-      ffi::Optional<ffi::Array<int64_t>> axes;
-
-      if (ndim == 2) {
-        // Pass none axes to permute_dims for simple transpose of 2D tensors.
-        axes = std::nullopt;
-      } else {
-        ffi::Array<int64_t> axes_array;
-        for (size_t i = 0; i < ndim; ++i) axes_array.push_back(i);
-        axes_array.Set(ndim - 1, ndim - 2);
-        axes_array.Set(ndim - 2, ndim - 1);
-        axes = ffi::Optional<ffi::Array<int64_t>>(axes_array);
-      }
-      return permute_dims(std::move(expr), axes);
-    };
-
-    auto transpose_shape_last_two_dims = [&](ffi::Array<PrimExpr>& shape) {
-      PrimExpr last_dim_shape = shape[shape.size() - 1];
-      shape.Set(shape.size() - 1, shape[shape.size() - 2]);
-      shape.Set(shape.size() - 2, last_dim_shape);
-    };
-
-    if (matches.count(pat_permuted_matmul_on_lhs)) {
-      if (!IsLastTwoDimsSwap(matches[pat_permuted_inner_matmul_on_lhs])) 
return expr;
-      if (shape_a.size() < 2 || shape_b.size() < 2) return expr;
-      expr_a = permute_last_two_dims(expr_a);
-      expr_b = permute_last_two_dims(expr_b);
-      transpose_shape_last_two_dims(shape_a);
-      transpose_shape_last_two_dims(shape_b);
-    } else if (matches.count(pat_permuted_matmul_on_rhs)) {
-      if (!IsLastTwoDimsSwap(matches[pat_permuted_inner_matmul_on_rhs])) 
return expr;
-      if (shape_b.size() < 2 || shape_c.size() < 2) return expr;
-      expr_b = permute_last_two_dims(expr_b);
-      expr_c = permute_last_two_dims(expr_c);
-      transpose_shape_last_two_dims(shape_b);
-      transpose_shape_last_two_dims(shape_c);
-    }
-
-    // If two of the three are compile-time, group those two values
-    // together, to allow them to be lifted out and pre-computed.
-    if (is_compile_time(expr_a) && is_compile_time(expr_b)) {
-      return matmul(matmul(expr_a, expr_b, std::nullopt), expr_c, out_dtype);
-    } else if (is_compile_time(expr_b) && is_compile_time(expr_c)) {
-      return matmul(expr_a, matmul(expr_b, expr_c, std::nullopt), out_dtype);
-    }
-
-    // Otherwise, select the order that reduces the total number of
-    // operations required, assuming a naive matmul (see below).
-
-    if (shape_a.size() == 1) {
-      shape_a = {IntImm(shape_a[0].ty(), 1), shape_a[0]};
-    }
-    if (shape_b.size() == 1) {
-      if (matches.count(pat_matmul_on_lhs)) {
-        shape_b = {shape_b[0], IntImm(shape_b[0].ty(), 1)};
-      } else if (matches.count(pat_matmul_on_rhs)) {
-        shape_b = {IntImm(shape_b[0].ty(), 1), shape_b[0]};
-      } else {
-        TVM_FFI_THROW(InternalError)
-            << "OrPattern " << pat << " matched, but neither " << 
pat_matmul_on_lhs << " nor "
-            << pat_matmul_on_rhs << " matched";
-      }
-    }
-    if (shape_c.size() == 1) {
-      shape_c = {shape_c[0], IntImm(shape_c[0].ty(), 1)};
-    }
-
-    PrimExpr size_N = shape_a[shape_a.size() - 2];  // row of A
-    PrimExpr size_R = shape_a[shape_a.size() - 1];  // col of A and row of B
-    PrimExpr size_M = shape_c[shape_c.size() - 2];  // row of C and col of B
-    PrimExpr size_B = shape_c[shape_c.size() - 1];  // col of C
-
-    sym::Analyzer analyzer;
-    auto prefix_a = GetBatchPrefix(shape_a);
-    auto prefix_b = GetBatchPrefix(shape_b);
-    auto prefix_c = GetBatchPrefix(shape_c);
-
-    auto opt_prefix_ab = InferBatchedMatmulBroadcastPrefix(analyzer.get(), 
prefix_a, prefix_b);
-    if (!opt_prefix_ab) return expr;
-    auto opt_prefix_bc = InferBatchedMatmulBroadcastPrefix(analyzer.get(), 
prefix_b, prefix_c);
-    if (!opt_prefix_bc) return expr;
-    auto opt_prefix_outer_lhs =
-        InferBatchedMatmulBroadcastPrefix(analyzer.get(), 
opt_prefix_ab.value(), prefix_c);
-    if (!opt_prefix_outer_lhs) return expr;
-    auto opt_prefix_outer_rhs =
-        InferBatchedMatmulBroadcastPrefix(analyzer.get(), prefix_a, 
opt_prefix_bc.value());
-    if (!opt_prefix_outer_rhs) return expr;
-
-    PrimExpr batch_ab = ProductDims(opt_prefix_ab.value());
-    PrimExpr batch_bc = ProductDims(opt_prefix_bc.value());
-    PrimExpr batch_outer_lhs = ProductDims(opt_prefix_outer_lhs.value());
-    PrimExpr batch_outer_rhs = ProductDims(opt_prefix_outer_rhs.value());
-
-    // Compare naive matmul FLOPs for two evaluation orders of
-    //   matmul(A, matmul(B, C))  vs  matmul(matmul(A, B), C)
-    //
-    // Matrix dims (last two axes): A [N, R], B [R, M], C [M, B_last]
-    // Each matmul uses the broadcasted batch prefix of its operands.
-    //
-    // LHS first — matmul(matmul(A, B), C):
-    //   batch_ab * N * R * M + batch_outer_lhs * N * M * B_last
-    PrimExpr ops_with_lhs_first =
-        batch_ab * size_N * size_R * size_M + batch_outer_lhs * size_N * 
size_M * size_B;
-    // RHS first — matmul(A, matmul(B, C)):
-    //   batch_bc * R * M * B_last + batch_outer_rhs * N * R * B_last
-    PrimExpr ops_with_rhs_first =
-        batch_bc * size_R * size_M * size_B + batch_outer_rhs * size_N * 
size_R * size_B;
-
-    
analyzer->rewrite_simplify.SetEnabledExtensions(static_cast<sym::RewriteSimplifier::Extension>(
-        analyzer->rewrite_simplify.GetEnabledExtensions() |
-        sym::RewriteSimplifier::Extension::kComparisonOfProductAndSum));
-    With<sym::ConstraintContext> func_attr_constraint(analyzer, 
symbolic_var_constraints);
-    With<sym::ConstraintContext> analyzer_constraint(
-        analyzer, batch_ab > 0 && batch_bc > 0 && batch_outer_lhs > 0 && 
batch_outer_rhs > 0 &&
-                      size_N > 0 && size_R > 0 && size_M > 0 && size_B > 0);
-
-    if (analyzer->CanProve(ops_with_lhs_first < ops_with_rhs_first)) {
-      return matmul(matmul(expr_a, expr_b, std::nullopt), expr_c, out_dtype);
-    } else if (analyzer->CanProve(ops_with_rhs_first < ops_with_lhs_first)) {
-      return matmul(expr_a, matmul(expr_b, expr_c, std::nullopt), out_dtype);
-    }
-
-    // If we cannot determine which order is best, keep the existing order.
-    return expr;
-  };
-
-  return {pat, rewriter};
-}
-
-}  // namespace
-
-namespace transform {
-Pass AdjustMatmulOrder() {
-  auto pass_func = [=](Function func, IRModule mod, PassContext pc) {
-    auto [pattern, rewriter] = CreatePatterns(func);
-    return RewriteCall(pattern, rewriter, func);
-  };
-  return CreateFunctionPass(pass_func, 1, "AdjustMatmulOrder");
-}
-
-TVM_FFI_STATIC_INIT_BLOCK() {
-  namespace refl = tvm::ffi::reflection;
-  refl::GlobalDef().def("relax.transform.AdjustMatmulOrder", 
AdjustMatmulOrder);
-}
-
-}  // namespace transform
-}  // namespace relax
-}  // namespace tvm
diff --git a/src/s_tir/transform/stmt_simplify.cc 
b/src/s_tir/transform/stmt_simplify.cc
index 2066cc6051..0f1c2cd4dc 100644
--- a/src/s_tir/transform/stmt_simplify.cc
+++ b/src/s_tir/transform/stmt_simplify.cc
@@ -94,7 +94,6 @@ class StmtSimplifier : public StmtExprMutatorWithAnalyzer {
 };
 
 Function StmtSimplifier::Run(Function func) {
-  
analyzer_->rewrite_simplify.SetEnabledExtensions(sym::RewriteSimplifier::kNone);
   MarkTensorParamShapes(func);
   auto* n = func.CopyOnWrite();
   // Shared string literals no longer enter the primitive analyzer.  Inline 
their
diff --git a/src/sym/analyzer.cc b/src/sym/analyzer.cc
index 129d0f755e..89a4f8b2ce 100644
--- a/src/sym/analyzer.cc
+++ b/src/sym/analyzer.cc
@@ -63,7 +63,7 @@ void AnalyzerObj::Bind(const Var& var, const PrimExpr& expr, 
bool allow_override
   this->rewrite_simplify.Update(var, new_expr, allow_override);
   this->canonical_simplify.Update(var, new_expr, allow_override);
   this->int_set.Update(var, this->int_set(new_expr), allow_override);
-  this->transitive_comparisons.Bind(var, expr, allow_override);
+  this->known_comparisons.Bind(var, expr, allow_override);
   this->z3_prover.Bind(var, expr, allow_override);
 }
 
@@ -74,7 +74,7 @@ void AnalyzerObj::Bind(const Var& var, const Range& range, 
bool allow_override)
   } else {
     this->const_int_bound.Bind(var, range, allow_override);
     this->int_set.Bind(var, range, allow_override);
-    this->transitive_comparisons.Bind(var, range, allow_override);
+    this->known_comparisons.Bind(var, range, allow_override);
     this->z3_prover.Bind(var, range, allow_override);
   }
   // skip modular_set
@@ -147,7 +147,7 @@ void ConstraintContext::EnterWithScope() {
   recovery_functions_.push_back(
       analyzer_->rewrite_simplify.EnterConstraint(constraint_, is_assume_));
   
recovery_functions_.push_back(analyzer_->int_set.EnterConstraint(constraint_));
-  
recovery_functions_.push_back(analyzer_->transitive_comparisons.EnterConstraint(constraint_));
+  
recovery_functions_.push_back(analyzer_->known_comparisons.EnterConstraint(constraint_));
   
recovery_functions_.push_back(analyzer_->z3_prover.EnterConstraint(constraint_, 
is_assume_));
 }
 
@@ -289,7 +289,7 @@ Analyzer AnalyzerObj::Clone() const {
   cloned->rewrite_simplify.CopyFrom(this->rewrite_simplify);
   cloned->canonical_simplify.CopyFrom(this->canonical_simplify);
   cloned->int_set.CopyFrom(this->int_set);
-  cloned->transitive_comparisons.CopyFrom(this->transitive_comparisons);
+  cloned->known_comparisons.CopyFrom(this->known_comparisons);
   cloned->z3_prover.CopyFrom(this->z3_prover);
   return cloned;
 }
@@ -377,10 +377,8 @@ TVM_FFI_STATIC_INIT_BLOCK() {
            })
       .def_method("sym.AnalyzerCanProveEqual", &AnalyzerObj::CanProveEqual)
       .def("sym.AnalyzerTryCompare",
-           [](Analyzer analyzer, const PrimExpr& lhs, const PrimExpr& rhs,
-              bool propagate_inequalities) {
-             return static_cast<int64_t>(
-                 analyzer->transitive_comparisons.TryCompare(lhs, rhs, 
propagate_inequalities));
+           [](Analyzer analyzer, const PrimExpr& lhs, const PrimExpr& rhs) {
+             return 
static_cast<int64_t>(analyzer->known_comparisons.TryCompare(lhs, rhs));
            })
       .def("sym.AnalyzerIsZ3Enabled",
            [](Analyzer analyzer) { return analyzer->z3_prover.IsEnabled(); })
@@ -397,15 +395,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
              analyzer->z3_prover.SetRLimit(static_cast<unsigned>(rlimit));
            })
       .def("sym.AnalyzerGetZ3Stats",
-           [](Analyzer analyzer) { return analyzer->z3_prover.GetStats(); })
-      .def("sym.AnalyzerGetEnabledExtensions",
-           [](Analyzer analyzer) {
-             return 
static_cast<std::int64_t>(analyzer->rewrite_simplify.GetEnabledExtensions());
-           })
-      .def("sym.AnalyzerSetEnabledExtensions", [](Analyzer analyzer, int64_t 
flags) {
-        analyzer->rewrite_simplify.SetEnabledExtensions(
-            static_cast<RewriteSimplifier::Extension>(flags));
-      });
+           [](Analyzer analyzer) { return analyzer->z3_prover.GetStats(); });
 }
 
 }  // namespace sym
diff --git a/src/sym/conjunctive_normal_form.cc 
b/src/sym/conjunctive_normal_form.cc
deleted file mode 100644
index 56f96d73cb..0000000000
--- a/src/sym/conjunctive_normal_form.cc
+++ /dev/null
@@ -1,445 +0,0 @@
-/*
- * 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 tvm/sym/conjunctive_normal_form.cc
- */
-
-#include "conjunctive_normal_form.h"
-
-#include <tvm/ir/prim/expr.h>
-#include <tvm/ir/prim/op.h>
-#include <tvm/sym/analyzer.h>
-
-#include <optional>
-#include <unordered_map>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-#include "pattern_match.h"
-#include "rewrite_simplify.h"
-
-namespace tvm {
-namespace sym {
-
-namespace {
-/* \brief A utility for simplifying expressions using conjunctive/disjuctive 
normal forms */
-class AndOfOrs {
- public:
-  /*! \brief Construct the simplifier
-   *
-   * Convert a PrimExpr to the internal representation.
-   *
-   * \param expr The PrimExpr to be simplified.
-   */
-  explicit AndOfOrs(const PrimExpr& expr);
-
-  /*! \brief Convert internal representation to PrimExpr */
-  PrimExpr ToPrimExpr() const;
-
-  /*! \brief Simplify the internal representation */
-  void Simplify(AnalyzerObj* analyzer);
-
- private:
-  /*! \brief Internal utility, simplify within each group of expressions
-   *
-   * For each pair of values within a chunk, attempt to simplify them into
-   * a single expression.
-   *
-   * For example,
-   *    before = (a == 5) && ((b < 10) || (b > 10))
-   *    after  = (a == 5) && ((b != 10) || false)
-   */
-  void SimplifyWithinChunks(AnalyzerObj* analyzer);
-
-  /*! \brief Internal utility, simplify across groups of expressions
-   *
-   * For each pair of chunks, if the two chunks differ by only a single
-   * term, attempt to simplify those differing terms.
-   *
-   * For example,
-   *    before = ((a == 5) || (b <= 10)) && ((a == 5) || (b >= 10))
-   *    after  = ((a == 5) || (b == 10)) && ((a == 5) || true)
-   */
-  void SimplifyAcrossChunks(AnalyzerObj* analyzer);
-
-  /*! \brief Remove instances of true/false from internal representation
-   *
-   * To avoid invalidating iterators, `SimplifyWithinChunks` and
-   * `SimplifyAcrossChunks` may replace keys, but may not remove keys
-   * from the internal representation.  For example, `(a < 5) && (a <
-   * 10)` would be simplified to `(a < 5) && true`.  The
-   * `RemoveTrueFalse` function removes these leftover instances of
-   * true/false.
-   */
-  void RemoveTrueFalse();
-
-  /*! \brief Internal utility function used to convert to internal form */
-  static void VisitAndExpressions(const PrimExpr& expr,
-                                  std::function<void(const PrimExpr&)> 
callback);
-  /*! \brief Internal utility function used to convert to internal form */
-  static void VisitOrExpressions(const PrimExpr& expr,
-                                 std::function<void(const PrimExpr&)> 
callback);
-
-  /* \brief Type-safe wrapper class that represents an PrimExpr
-   *
-   * Because integer indices are used frequently through this class,
-   * maintaining a separation between integer indices used to access
-   * specific elements of the internal representation, and unique
-   * identifiers used to represent expressions PrimExpr, is useful.
-   */
-  enum class Key : size_t {};
-
-  /*! \brief Convert a PrimExpr to a Key */
-  Key GetKey(const PrimExpr& expr);
-
-  /*! \brief Convert a Key to a PrimExpr */
-  PrimExpr GetExpr(Key key) const;
-
-  /*! \brief Attempt to simplify (a && b)
-   *
-   * If successful, will overwrite the parameters `a` and `b` with the
-   * simplified form.
-   */
-  void TrySimplifyOr(Key* a, Key* b, AnalyzerObj* analyzer);
-
-  /*! \brief Attempt to simplify (a || b)
-   *
-   * If successful, will overwrite the parameters `a` and `b` with the
-   * simplified form.
-   */
-  void TrySimplifyAnd(Key* a, Key* b, AnalyzerObj* analyzer);
-
-  /*! \brief The internal representation
-   *
-   * `chunks[i][j]` is the j-th expression in the i-th OR-group.
-   */
-  std::vector<std::vector<Key>> chunks_;
-
-  /*! \brief Mapping from internal Key to PrimExpr */
-  std::unordered_map<Key, PrimExpr, ffi::StructuralHash, ffi::StructuralEqual> 
key_to_expr_;
-
-  /*! \brief Mapping from PrimExpr to internal Key */
-  std::unordered_map<PrimExpr, Key, ffi::StructuralHash, ffi::StructuralEqual> 
expr_to_key_;
-
-  /*! \brief Cached key representing IntImm::Bool(true) */
-  Key key_true_;
-
-  /*! \brief Cached key representing IntImm::Bool(false) */
-  Key key_false_;
-};
-
-AndOfOrs::AndOfOrs(const PrimExpr& expr)
-    : key_true_(GetKey(IntImm::Bool(true))), 
key_false_(GetKey(IntImm::Bool(false))) {
-  VisitAndExpressions(expr, [&](const PrimExpr& outer_expr) {
-    std::vector<Key> or_components;
-    VisitOrExpressions(outer_expr, [&](const PrimExpr& inner_expr) {
-      Key key = GetKey(inner_expr);
-      bool is_duplicate = std::any_of(or_components.begin(), 
or_components.end(),
-                                      [&](Key prev) { return prev == key; });
-      if (!is_duplicate) {
-        or_components.push_back(key);
-      }
-    });
-
-    bool is_permutation =
-        std::any_of(chunks_.begin(), chunks_.end(), [&](const 
std::vector<Key>& prev_components) {
-          return or_components.size() == prev_components.size() &&
-                 std::is_permutation(prev_components.begin(), 
prev_components.end(),
-                                     or_components.begin());
-        });
-    if (!is_permutation) {
-      chunks_.push_back(std::move(or_components));
-    }
-  });
-}
-
-void AndOfOrs::VisitAndExpressions(const PrimExpr& expr,
-                                   std::function<void(const PrimExpr&)> 
callback) {
-  PVar<PrimExpr> x, y, z;
-  if ((x && y).Match(expr)) {
-    // These are separate AND conditions, recurse into them in case
-    // they contain AND internally.
-    VisitAndExpressions(x.Eval(), callback);
-    VisitAndExpressions(y.Eval(), callback);
-  } else if ((x || y).Match(expr)) {
-    // This may be the bottom-most breakdown, but either x or y may
-    // themselves contain AND.  (e.g. (A && B) || (C && D) should be
-    // split into (A || C), (A || D), (B || C), and (B || D).)
-    // Recurse into each, then reconstruct an OR condition.
-    VisitAndExpressions(x.Eval(), [&](const PrimExpr& x_part) {
-      VisitAndExpressions(y.Eval(), [&](const PrimExpr& y_part) { 
callback(x_part || y_part); });
-    });
-  } else {
-    // This is bottom-most breakdown.
-    callback(expr);
-  }
-}
-
-void AndOfOrs::VisitOrExpressions(const PrimExpr& expr,
-                                  std::function<void(const PrimExpr&)> 
callback) {
-  PVar<PrimExpr> x, y, z;
-  if ((x || y).Match(expr)) {
-    // These are separate OR conditions, recurse into them in case
-    // they contain OR internally.
-    VisitOrExpressions(x.Eval(), callback);
-    VisitOrExpressions(y.Eval(), callback);
-  } else if ((x && y).Match(expr)) {
-    // This may be the bottom-most breakdown, but either x or y may
-    // themselves contain OR.  (e.g. (A || B) && (C || D) should be
-    // split into (A && C), (A && D), (B && C), and (B && D).)
-    // Recurse into each, then reconstruct an AND condition.
-    VisitOrExpressions(x.Eval(), [&](const PrimExpr& x_part) {
-      VisitOrExpressions(y.Eval(), [&](const PrimExpr& y_part) { 
callback(x_part && y_part); });
-    });
-  } else {
-    // This is bottom-most breakdown.
-    callback(expr);
-  }
-}
-
-AndOfOrs::Key AndOfOrs::GetKey(const PrimExpr& expr) {
-  auto it = expr_to_key_.find(expr);
-  if (it != expr_to_key_.end()) {
-    return it->second;
-  }
-
-  Key key{expr_to_key_.size()};
-  expr_to_key_[expr] = key;
-  key_to_expr_.insert_or_assign(key, expr);
-  return key;
-}
-
-PrimExpr AndOfOrs::GetExpr(AndOfOrs::Key key) const {
-  auto it = key_to_expr_.find(key);
-  TVM_FFI_ICHECK(it != key_to_expr_.end());
-  return it->second;
-}
-
-PrimExpr AndOfOrs::ToPrimExpr() const {
-  PrimExpr expr = IntImm::Bool(true);
-  for (const auto& chunk : chunks_) {
-    PrimExpr chunk_expr = IntImm::Bool(false);
-    for (Key j : chunk) {
-      chunk_expr = chunk_expr || GetExpr(j);
-    }
-    expr = expr && chunk_expr;
-  }
-  return expr;
-}
-
-void AndOfOrs::TrySimplifyOr(Key* a_ptr, Key* b_ptr, AnalyzerObj* analyzer) {
-  Key& a = *a_ptr;
-  Key& b = *b_ptr;
-  PrimExpr joint = GetExpr(a) || GetExpr(b);
-  PrimExpr simplified = analyzer->rewrite_simplify(joint);
-  if (!prim::ExprDeepEqual()(simplified, joint)) {
-    if (auto* simplified_or = simplified.as<prim::OrNode>()) {
-      a = GetKey(simplified_or->a);
-      b = GetKey(simplified_or->b);
-    } else {
-      a = key_false_;
-      b = GetKey(simplified);
-    }
-  }
-}
-
-void AndOfOrs::TrySimplifyAnd(Key* a_ptr, Key* b_ptr, AnalyzerObj* analyzer) {
-  Key& a = *a_ptr;
-  Key& b = *b_ptr;
-  PrimExpr joint = GetExpr(a) && GetExpr(b);
-  PrimExpr simplified = analyzer->rewrite_simplify(joint);
-  if (!prim::ExprDeepEqual()(simplified, joint)) {
-    if (auto* simplified_and = simplified.as<prim::AndNode>()) {
-      a = GetKey(simplified_and->a);
-      b = GetKey(simplified_and->b);
-    } else {
-      a = key_true_;
-      b = GetKey(simplified);
-    }
-  }
-}
-
-void AndOfOrs::Simplify(AnalyzerObj* analyzer) {
-  SimplifyWithinChunks(analyzer);
-  RemoveTrueFalse();
-  SimplifyAcrossChunks(analyzer);
-  RemoveTrueFalse();
-}
-
-void AndOfOrs::SimplifyWithinChunks(AnalyzerObj* analyzer) {
-  for (auto& chunk : chunks_) {
-    for (size_t expr_i = 0; expr_i < chunk.size(); expr_i++) {
-      for (size_t expr_j = expr_i + 1; expr_j < chunk.size(); expr_j++) {
-        Key& key_i = chunk[expr_i];
-        Key& key_j = chunk[expr_j];
-
-        TrySimplifyOr(&key_i, &key_j, analyzer);
-      }
-    }
-  }
-}
-
-void AndOfOrs::SimplifyAcrossChunks(AnalyzerObj* analyzer) {
-  for (size_t i_and = 0; i_and < chunks_.size(); i_and++) {
-    for (size_t j_and = i_and + 1; j_and < chunks_.size(); j_and++) {
-      auto& i_chunk = chunks_[i_and];
-      auto& j_chunk = chunks_[j_and];
-
-      if (i_chunk.size() == 1 && j_chunk.size() == 1) {
-        auto& key_i = i_chunk[0];
-        auto& key_j = j_chunk[0];
-        TrySimplifyAnd(&key_i, &key_j, analyzer);
-        continue;
-      }
-      std::unordered_set<Key> j_set(j_chunk.begin(), j_chunk.end());
-
-      std::optional<size_t> i_distinct_index;
-      for (size_t i = 0; i < i_chunk.size(); i++) {
-        if (!j_set.count(i_chunk[i])) {
-          i_distinct_index = i;
-          break;
-        }
-      }
-
-      if (!i_distinct_index.has_value()) {
-        // I = (i_0 || i_1 || ... || i_N)
-        // J = (i_0 || i_1 || ... || i_N || j_0 || ... || j_N)
-        // I && J == I == I && true
-
-        j_chunk = {key_true_};
-        continue;
-      }
-
-      std::unordered_set<Key> i_set(i_chunk.begin(), i_chunk.end());
-
-      std::optional<size_t> j_distinct_index;
-      for (size_t j = 0; j < j_chunk.size(); j++) {
-        if (!i_set.count(j_chunk[j])) {
-          j_distinct_index = j;
-          break;
-        }
-      }
-
-      if (!j_distinct_index.has_value()) {
-        // I = (i_0 || ... || i_N || j_0 || ... || j_N)
-        // J = (j_0 || ... || j_N)
-        // I && J == J == true && J
-
-        i_chunk = {key_true_};
-        continue;
-      }
-
-      if (i_chunk.size() == j_chunk.size()) {
-        size_t num_shared_exprs = 0;
-        for (const auto& j_key : j_chunk) {
-          if (i_set.count(j_key)) {
-            ++num_shared_exprs;
-          }
-        }
-
-        if (num_shared_exprs + 1 == i_chunk.size()) {
-          // All but one of the expressions are shared.  If the AND
-          // of the distinct expressions can be simplified, we can
-          // replace.
-          //
-          // (A or B) and (A or C) => A or (B and C)
-          auto& key_i = i_chunk[i_distinct_index.value()];
-          auto& key_j = j_chunk[j_distinct_index.value()];
-
-          // When attempting to simplify (B and C), the analyzer may
-          // assume that A is false.
-          PrimExpr known = [&]() {
-            PrimExpr known = IntImm::Bool(true);
-            for (const auto& key : i_chunk) {
-              if (&key != &key_i) {
-                known = known && analyzer->Simplify(!GetExpr(key));
-              }
-            }
-            return known;
-          }();
-
-          With<ConstraintContext> context(analyzer, known);
-          TrySimplifyAnd(&key_i, &key_j, analyzer);
-        }
-      }
-    }
-  }
-}
-
-void AndOfOrs::RemoveTrueFalse() {
-  for (auto& chunk : chunks_) {
-    // Any occurrence of True inside an OR makes the entire expression True.
-    if (std::any_of(chunk.begin(), chunk.end(), [&](Key key) { return key == 
key_true_; })) {
-      chunk = {key_true_};
-    } else {
-      // Any occurrence of False inside an OR can be removed
-      chunk.erase(
-          std::remove_if(chunk.begin(), chunk.end(), [&](Key key) { return key 
== key_false_; }),
-          chunk.end());
-    }
-  }
-
-  // Any occurence of False inside an AND makes the entire expression False.
-  if (std::any_of(chunks_.begin(), chunks_.end(),
-                  [&](const std::vector<Key>& chunk) { return chunk.size() == 
0; })) {
-    chunks_ = {{}};
-  } else {
-    // Any occurrence of True inside an AND can be removed.
-    chunks_.erase(std::remove_if(chunks_.begin(), chunks_.end(),
-                                 [&](const std::vector<Key>& chunk) {
-                                   return chunk.size() == 1 && chunk[0] == 
key_true_;
-                                 }),
-                  chunks_.end());
-  }
-}
-
-// Helper utility for temporarily disabling the
-// kConvertBooleanToAndOfOrs flag on an analyzer, to prevent infinite
-// recursion.
-class DisableAndOfOrRecursion {
- public:
-  explicit DisableAndOfOrRecursion(AnalyzerObj* analyzer)
-      : analyzer_(analyzer), 
cached_flags_(analyzer->rewrite_simplify.GetEnabledExtensions()) {
-    auto new_flags = static_cast<RewriteSimplifier::Extension>(
-        cached_flags_ & (~RewriteSimplifier::kConvertBooleanToAndOfOrs));
-    analyzer->rewrite_simplify.SetEnabledExtensions(new_flags);
-  }
-  ~DisableAndOfOrRecursion() { 
analyzer_->rewrite_simplify.SetEnabledExtensions(cached_flags_); }
-
-  DisableAndOfOrRecursion(const DisableAndOfOrRecursion&) = delete;
-  DisableAndOfOrRecursion& operator=(const DisableAndOfOrRecursion&) = delete;
-
- private:
-  AnalyzerObj* analyzer_;
-  RewriteSimplifier::Extension cached_flags_;
-};
-
-}  // namespace
-
-PrimExpr SimplifyAsAndOfOrs(const PrimExpr& expr, AnalyzerObj* analyzer) {
-  DisableAndOfOrRecursion context(analyzer);
-  AndOfOrs repr(analyzer->Simplify(expr));
-  repr.Simplify(analyzer);
-  return repr.ToPrimExpr();
-}
-
-}  // namespace sym
-}  // namespace tvm
diff --git a/src/sym/conjunctive_normal_form.h 
b/src/sym/conjunctive_normal_form.h
deleted file mode 100644
index 489feef7ed..0000000000
--- a/src/sym/conjunctive_normal_form.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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 conjunctive_normal_form.h
- *
- * \brief Centralized location for simplifying into specific forms
- */
-
-#ifndef TVM_SYM_CONJUNCTIVE_NORMAL_FORM_H_
-#define TVM_SYM_CONJUNCTIVE_NORMAL_FORM_H_
-
-#include <tvm/ir/prim/expr.h>
-
-namespace tvm {
-namespace sym {
-
-class AnalyzerObj;
-class Analyzer;
-
-/*! \brief Convert boolean expression to AND of ORs and simplify
- *
- * \param expr The PrimExpr to be simplified
- *
- * \param analyzer The analyzer with which to simplify
- *
- * \return The simplified expression
- */
-PrimExpr SimplifyAsAndOfOrs(const PrimExpr& expr, AnalyzerObj* analyzer);
-
-}  // namespace sym
-}  // namespace tvm
-
-#endif  // TVM_SYM_CONJUNCTIVE_NORMAL_FORM_H_
diff --git a/src/sym/transitive_comparison_analyzer.cc 
b/src/sym/known_comparison_analyzer.cc
similarity index 53%
rename from src/sym/transitive_comparison_analyzer.cc
rename to src/sym/known_comparison_analyzer.cc
index a1b128467b..16ae5f69b8 100644
--- a/src/sym/transitive_comparison_analyzer.cc
+++ b/src/sym/known_comparison_analyzer.cc
@@ -17,14 +17,13 @@
  * under the License.
  */
 /*!
- * \file tvm/sym/transitive_comparison_analyzer.cc
+ * \file tvm/sym/known_comparison_analyzer.cc
  */
 
 #include <tvm/ir/prim/expr.h>
 #include <tvm/sym/analyzer.h>
 
 #include <optional>
-#include <unordered_set>
 #include <vector>
 
 #include "constraint_extract.h"
@@ -36,7 +35,7 @@ using namespace tvm::prim;
 
 using prim::IsConstInt;
 
-class TransitiveComparisonAnalyzer::Impl {
+class KnownComparisonAnalyzer::Impl {
  public:
   /* \brief Using previously specified knowns, compare the expressions provided
    *
@@ -44,19 +43,12 @@ class TransitiveComparisonAnalyzer::Impl {
    *
    * \param rhs The right-hand side of the comparison
    *
-   * \param propagate_inequalities If true, attempt to find a sequence
-   * of transitive inequalities that allow the lhs and rhs to be
-   * compared.  If false, only use the known comparison that have been
-   * directly provided.  Using `propagate_inequalities = false` is
-   * roughly equivalent to comparing against all known values with
-   * `prim::ExprDeepEqual`, but also allowing for constant offsets on either
-   * side of the inequality.
+   * Directly known comparisons also allow constant offsets on either side.
    *
    * \return The most specific result that can be proven about the
    * comparison.  If nothing can be proven, returns kUnknown.
    */
-  CompareResult TryCompare(const PrimExpr& lhs, const PrimExpr& rhs,
-                           bool propagate_inequalities = true) const;
+  CompareResult TryCompare(const PrimExpr& lhs, const PrimExpr& rhs) const;
 
   /*! \brief Bind a variable as being equal to a known expression
    *
@@ -156,11 +148,6 @@ class TransitiveComparisonAnalyzer::Impl {
      */
     Comparison(Key lhs, Key rhs, int64_t offset, CompareResult result);
 
-    /*! \brief Utility function to validate that all GT and LT results
-     *  have been normalized out
-     */
-    bool IsNormalized() const;
-
     /*! \brief Move the specified expression to the LHS.
      *
      * \param new_lhs The argument that should be moved to the LHS of the
@@ -172,17 +159,6 @@ class TransitiveComparisonAnalyzer::Impl {
      */
     std::optional<Comparison> WithLHS(Key new_lhs) const;
 
-    /*! \brief Create the negation of the current comparison */
-    Comparison Negated() const;
-
-    /*! \brief Check the this comparison implies
-     *
-     * Returns true if this comparison being true implies that the
-     * other comparison must also be true.  Returns false if the other
-     * comparison cannot be shown to be true.
-     */
-    bool Implies(const Comparison& other) const;
-
     // The LHS of the comparison
     Key lhs_;
 
@@ -224,51 +200,11 @@ class TransitiveComparisonAnalyzer::Impl {
    */
   std::vector<Comparison> CollectDirectComparisons(Key lhs_key, Key rhs_key) 
const;
 
-  /*! Collect known comparisons between LHS and RHS, with propagation
-   *
-   * \param lhs_key The left-hand side of the comparison
-   *
-   * \param rhs_key The right-hand side of the comparison
-   *
-   * \returns All comparisons between `lhs_key` and `rhs_key`,
-   * including the explicitly-provided comparisons in `knowns_` and
-   * `scoped_knowns_`, and comparisons provable through a series of
-   * comparisons through other values.  All comparisons returned are
-   * between `lhs_key` and `rhs_key`, and are normalized such that
-   * `lhs_key` is on the left-hand side.
-   */
-  std::vector<Comparison> CollectIndirectComparisons(Key lhs_key, Key rhs_key) 
const;
-
-  /*! \brief Internal function used by CollectIndirectComparisons
-   *
-   * Perform a depth-first search through the space of known
-   * expressions, starting at the LHS of a comparison.  In this
-   * search, each expression is a node of a graph, and each known
-   * comparison is an edge of the graph.
-   *
-   * For example, suppose we have previous knowns of (A<=B), (B<=C+1)
-   * and (C<=D-5).  The expressions [A,B,C,D] are the nodes of the
-   * search space.  Each comparison is an edge connecting two
-   * expressions, such as (B<=C+1) connecting the expressions B and D.
-   * If we are attempting to compare expressions A and D, a search
-   * starting at expression A could follow each edge until reaching
-   * expression D, then combine the comparisons that compose the path
-   * into the expression A<=D-4.
-   *
-   * \param lhs_key The left-hand side of the comparison
-   *
-   * \param rhs_key The right-hand side of the comparison
-   *
-   * \returns A vector of comparisons between the two expressions.
-   */
-  std::vector<Comparison> DFSFromLHS(Key lhs_key, Key rhs_key) const;
-
   /*! \brief Combine a set of comparisons that share a LHS and RHS
    *
    * \param lhs_to_rhs The comparisons to merge.  These should all
    * have the same LHS and RHS.  This parameter will typically be the
-   * result from `CollectDirectComparisons` or
-   * `CollectIndirectComparisons`.
+   * result from `CollectDirectComparisons`.
    *
    * \param offset The constant offset in the comparison being proven.
    * This is extracted from any additive/subtractive constants in the
@@ -332,19 +268,6 @@ CompareResult Reverse(CompareResult res) {
   }
 }
 
-// Internal utility, return the CompareResult resulting from negating
-// the comparison.
-CompareResult Negate(CompareResult res) {
-  switch (res) {
-    case CompareResult::kInconsistent:
-      return CompareResult::kInconsistent;
-    case CompareResult::kUnknown:
-      return CompareResult::kUnknown;
-    default:
-      return CompareResult(~static_cast<int>(res) & 
static_cast<int>(CompareResult::kUnknown));
-  }
-}
-
 // Internal utility, extract constant offsets out of the two sides of
 // a comparison.  Given lhs and rhs, return a tuple of three elements
 // (lhs_inner, rhs_inner, offset), such that (lhs OP rhs) and
@@ -374,8 +297,8 @@ std::tuple<PrimExpr, PrimExpr, int64_t> 
ExtractOffsets(const PrimExpr& lhs, cons
 
 }  // namespace
 
-std::optional<TransitiveComparisonAnalyzer::Impl::Comparison>
-TransitiveComparisonAnalyzer::Impl::FromExpr(const PrimExpr& expr) {
+std::optional<KnownComparisonAnalyzer::Impl::Comparison> 
KnownComparisonAnalyzer::Impl::FromExpr(
+    const PrimExpr& expr) {
   CompareResult res;
   PVar<PrimExpr> x, y;
   if ((x <= y).Match(expr)) {
@@ -412,32 +335,10 @@ TransitiveComparisonAnalyzer::Impl::FromExpr(const 
PrimExpr& expr) {
   return Comparison(lhs_key, rhs_key, offset, res);
 }
 
-TransitiveComparisonAnalyzer::Impl::Comparison::Comparison(Key lhs, Key rhs, 
int64_t offset,
-                                                           CompareResult 
result)
+KnownComparisonAnalyzer::Impl::Comparison::Comparison(Key lhs, Key rhs, 
int64_t offset,
+                                                      CompareResult result)
     : lhs_(lhs), rhs_(rhs), offset_(offset), result_(result) {
-  // Normalize the comparison to remove LT and GT expressions,
-  // reducing the number of operators that must be handled later.  By
-  // eliminating LT and GT, instead of eliminating LE or GE, a
-  // potential off-by-one error is avoided.
-  //
-  // For floating-point numbers, (x < y + c1) and (y < z + c2) implies
-  // that (x < z + (c1 + c2)).  For integer types, which the
-  // TransitiveComparisonAnalyzer is intended for use with integers,
-  // LT or GT can give a tighter constraint, though with a less
-  // convenient symmetry.
-  //
-  // i < j + c1, j < k + c2
-  // i <= j + c1 - 1, j <= k + c2 - 1
-  // i + 1 - c1 <= j, j <= k + c2 - 1
-  // i + 1 - c1 <= k + c2 - 1
-  // i <= k + c1 + c2 - 2
-  // i < k + (c1 + c2 - 1)
-  //
-  // By always working with LE and GE comparisons, we avoid needing to
-  // handle the offset of one that would be introduced by LT and GT at
-  // all points of use.  The only point of use for LT and GT is when
-  // normalizing comparisons (i.e. this constructor).
-
+  // Integer strict comparisons become non-strict comparisons with a unit 
offset.
   if (result_ == CompareResult::kLT) {
     result_ = CompareResult::kLE;
     offset_ -= 1;
@@ -448,8 +349,8 @@ 
TransitiveComparisonAnalyzer::Impl::Comparison::Comparison(Key lhs, Key rhs, int
   }
 }
 
-std::optional<TransitiveComparisonAnalyzer::Impl::Key>
-TransitiveComparisonAnalyzer::Impl::ExprToPreviousKey(const PrimExpr& expr) 
const {
+std::optional<KnownComparisonAnalyzer::Impl::Key> 
KnownComparisonAnalyzer::Impl::ExprToPreviousKey(
+    const PrimExpr& expr) const {
   auto it = expr_to_key.find(expr);
   if (it != expr_to_key.end()) {
     return it->second;
@@ -458,8 +359,7 @@ TransitiveComparisonAnalyzer::Impl::ExprToPreviousKey(const 
PrimExpr& expr) cons
   }
 }
 
-TransitiveComparisonAnalyzer::Impl::Key 
TransitiveComparisonAnalyzer::Impl::ExprToKey(
-    const PrimExpr& expr) {
+KnownComparisonAnalyzer::Impl::Key 
KnownComparisonAnalyzer::Impl::ExprToKey(const PrimExpr& expr) {
   if (auto prev = ExprToPreviousKey(expr)) {
     return prev.value();
   } else {
@@ -469,14 +369,8 @@ TransitiveComparisonAnalyzer::Impl::Key 
TransitiveComparisonAnalyzer::Impl::Expr
   }
 }
 
-bool TransitiveComparisonAnalyzer::Impl::Comparison::IsNormalized() const {
-  // These < and > should be removed during normalization.  See the
-  // `Comparison::Comparison` constructor for further details.
-  return result_ != CompareResult::kLT && result_ != CompareResult::kGT;
-}
-
-std::optional<TransitiveComparisonAnalyzer::Impl::Comparison>
-TransitiveComparisonAnalyzer::Impl::Comparison::WithLHS(Key new_lhs) const {
+std::optional<KnownComparisonAnalyzer::Impl::Comparison>
+KnownComparisonAnalyzer::Impl::Comparison::WithLHS(Key new_lhs) const {
   if (new_lhs == lhs_) {
     return *this;
   } else if (new_lhs == rhs_) {
@@ -488,85 +382,29 @@ 
TransitiveComparisonAnalyzer::Impl::Comparison::WithLHS(Key new_lhs) const {
   }
 }
 
-TransitiveComparisonAnalyzer::Impl::Comparison
-TransitiveComparisonAnalyzer::Impl::Comparison::Negated() const {
-  return Comparison(lhs_, rhs_, offset_, Negate(result_));
-}
-
-bool TransitiveComparisonAnalyzer::Impl::Comparison::Implies(
-    const TransitiveComparisonAnalyzer::Impl::Comparison& other) const {
-  TVM_FFI_ICHECK(lhs_ == other.lhs_);
-  TVM_FFI_ICHECK(rhs_ == other.rhs_);
-  TVM_FFI_ICHECK(IsNormalized());
-  TVM_FFI_ICHECK(other.IsNormalized());
-
-  if (result_ == other.result_ && offset_ == other.offset_) {
-    // if c1 == c2, x != y + c1 => x != y + c2
-    // if c1 == c2, x == y + c1 => x == y + c2
-    return true;
-  }
-
-  if (other.result_ == CompareResult::kLE && offset_ <= other.offset_) {
-    if (result_ == CompareResult::kEQ || result_ == CompareResult::kLE) {
-      // if c1 <= c2, x <= y + c1 => x <= y + c2
-      // if c1 <= c2, x == y + c1 => x <= y + c2
-      return true;
-    }
-  }
-
-  if (other.result_ == CompareResult::kGE && offset_ >= other.offset_) {
-    if (result_ == CompareResult::kEQ || result_ == CompareResult::kGE) {
-      // if c1 >= c2, x == y + c1 => x >= y + c2
-      // if c1 >= c2, x >= y + c1 => x >= y + c2
-      return true;
-    }
-  }
-
-  if (other.result_ == CompareResult::kNE) {
-    if (result_ == CompareResult::kEQ && offset_ != other.offset_) {
-      // if c1 != c2, x == y + c1 => x != y + c2
-      return true;
-    }
-
-    if (result_ == CompareResult::kLE && offset_ < other.offset_) {
-      // if c1 < c2, x <= y + c1 => x < y + c2 => x != y + c2
-      return true;
-    }
-
-    if (result_ == CompareResult::kGE && offset_ > other.offset_) {
-      // if c1 != c2, x >= y + c1 => x > y + c2 => x != y + c2
-      return true;
-    }
-  }
+KnownComparisonAnalyzer::KnownComparisonAnalyzer() : 
impl_(std::make_unique<Impl>()) {}
+KnownComparisonAnalyzer::~KnownComparisonAnalyzer() {}
 
-  return false;
-}
-
-TransitiveComparisonAnalyzer::TransitiveComparisonAnalyzer() : 
impl_(std::make_unique<Impl>()) {}
-TransitiveComparisonAnalyzer::~TransitiveComparisonAnalyzer() {}
-
-void TransitiveComparisonAnalyzer::CopyFrom(const 
TransitiveComparisonAnalyzer& other) {
+void KnownComparisonAnalyzer::CopyFrom(const KnownComparisonAnalyzer& other) {
   impl_->CopyFrom(*other.impl_);
 }
 
-CompareResult TransitiveComparisonAnalyzer::TryCompare(const PrimExpr& lhs, 
const PrimExpr& rhs,
-                                                       bool 
propagate_inequalities) {
-  return impl_->TryCompare(lhs, rhs, propagate_inequalities);
+CompareResult KnownComparisonAnalyzer::TryCompare(const PrimExpr& lhs, const 
PrimExpr& rhs) {
+  return impl_->TryCompare(lhs, rhs);
 }
 
-void TransitiveComparisonAnalyzer::Bind(const Var& var, const PrimExpr& expr, 
bool allow_override) {
+void KnownComparisonAnalyzer::Bind(const Var& var, const PrimExpr& expr, bool 
allow_override) {
   impl_->Bind(var, expr, allow_override);
 }
-void TransitiveComparisonAnalyzer::Bind(const Var& var, const Range& range, 
bool allow_override) {
+void KnownComparisonAnalyzer::Bind(const Var& var, const Range& range, bool 
allow_override) {
   impl_->Bind(var, range, allow_override);
 }
 
-std::function<void()> TransitiveComparisonAnalyzer::EnterConstraint(const 
PrimExpr& constraint) {
+std::function<void()> KnownComparisonAnalyzer::EnterConstraint(const PrimExpr& 
constraint) {
   return impl_->EnterConstraint(constraint);
 }
 
-void TransitiveComparisonAnalyzer::Impl::AddKnown(const PrimExpr& expr,
-                                                  std::vector<Comparison>* 
vec) {
+void KnownComparisonAnalyzer::Impl::AddKnown(const PrimExpr& expr, 
std::vector<Comparison>* vec) {
   for (const auto& subexpr : ExtractConstraints(expr, false)) {
     if (SideEffect(expr) <= CallEffectKind::kPure) {
       if (auto cmp = FromExpr(subexpr)) {
@@ -576,8 +414,7 @@ void TransitiveComparisonAnalyzer::Impl::AddKnown(const 
PrimExpr& expr,
   }
 }
 
-void TransitiveComparisonAnalyzer::Impl::Bind(const Var& var, const Range& 
range,
-                                              bool allow_override) {
+void KnownComparisonAnalyzer::Impl::Bind(const Var& var, const Range& range, 
bool allow_override) {
   auto it = prev_bindings_.find(var);
   if (it != prev_bindings_.end()) {
     prim::ExprDeepEqual expr_equal;
@@ -605,12 +442,12 @@ void TransitiveComparisonAnalyzer::Impl::Bind(const Var& 
var, const Range& range
   }
 }
 
-void TransitiveComparisonAnalyzer::Impl::Bind(const Var& var, const PrimExpr& 
expr,
-                                              bool allow_override) {
+void KnownComparisonAnalyzer::Impl::Bind(const Var& var, const PrimExpr& expr,
+                                         bool allow_override) {
   Bind(var, Range::FromMinExtent(expr, 1), allow_override);
 }
 
-std::function<void()> 
TransitiveComparisonAnalyzer::Impl::EnterConstraint(const PrimExpr& expr) {
+std::function<void()> KnownComparisonAnalyzer::Impl::EnterConstraint(const 
PrimExpr& expr) {
   size_t old_literal_size = scoped_knowns_.size();
   AddKnown(expr, &scoped_knowns_);
   size_t new_literal_size = scoped_knowns_.size();
@@ -622,9 +459,8 @@ std::function<void()> 
TransitiveComparisonAnalyzer::Impl::EnterConstraint(const
   return frecover;
 }
 
-CompareResult TransitiveComparisonAnalyzer::Impl::TryCompare(const PrimExpr& 
lhs_expr,
-                                                             const PrimExpr& 
rhs_expr,
-                                                             bool 
propagate_inequalities) const {
+CompareResult KnownComparisonAnalyzer::Impl::TryCompare(const PrimExpr& 
lhs_expr,
+                                                        const PrimExpr& 
rhs_expr) const {
   // Currently only supports integer checks
   if (!lhs_expr.ty().MatchesCode(DLDataTypeCode::kDLInt) ||
       !rhs_expr.ty().MatchesCode(DLDataTypeCode::kDLInt)) {
@@ -653,18 +489,12 @@ CompareResult 
TransitiveComparisonAnalyzer::Impl::TryCompare(const PrimExpr& lhs
     return CompareResult::kUnknown;
   }
 
-  auto lhs_to_rhs = [&]() {
-    if (propagate_inequalities) {
-      return CollectIndirectComparisons(lhs_key.value(), rhs_key.value());
-    } else {
-      return CollectDirectComparisons(lhs_key.value(), rhs_key.value());
-    }
-  }();
+  auto lhs_to_rhs = CollectDirectComparisons(lhs_key.value(), rhs_key.value());
   return MergeComparisons(lhs_to_rhs, offset);
 }
 
-std::vector<TransitiveComparisonAnalyzer::Impl::Comparison>
-TransitiveComparisonAnalyzer::Impl::CollectDirectComparisons(Key lhs_key, Key 
rhs_key) const {
+std::vector<KnownComparisonAnalyzer::Impl::Comparison>
+KnownComparisonAnalyzer::Impl::CollectDirectComparisons(Key lhs_key, Key 
rhs_key) const {
   std::vector<Comparison> output;
 
   auto append_known = [&](Comparison cmp) {
@@ -685,161 +515,7 @@ 
TransitiveComparisonAnalyzer::Impl::CollectDirectComparisons(Key lhs_key, Key rh
   return output;
 }
 
-std::vector<TransitiveComparisonAnalyzer::Impl::Comparison>
-TransitiveComparisonAnalyzer::Impl::CollectIndirectComparisons(Key lhs_key, 
Key rhs_key) const {
-  auto output = DFSFromLHS(lhs_key, rhs_key);
-  for (Comparison cmp : DFSFromLHS(rhs_key, lhs_key)) {
-    auto opt_normalized = cmp.WithLHS(lhs_key);
-    TVM_FFI_ICHECK(opt_normalized.has_value());
-    output.push_back(opt_normalized.value());
-  }
-  return output;
-}
-
-std::vector<TransitiveComparisonAnalyzer::Impl::Comparison>
-TransitiveComparisonAnalyzer::Impl::DFSFromLHS(Key lhs_key, Key rhs_key) const 
{
-  // Everything in `to_visit` has lhs as its lhs.
-  std::unordered_set<Key> seen;
-  std::unordered_set<Key> to_visit;
-  std::unordered_map<Key, std::vector<Comparison>> compared_to_lhs;
-
-  // Utility function to add a new known statement
-  auto declare_known = [&](Comparison cmp) {
-    std::vector<Comparison>& knowns = compared_to_lhs[cmp.rhs_];
-
-    // The comparison adds no new information, no modification
-    // required.
-    for (auto& prev_known : knowns) {
-      if (prev_known.Implies(cmp)) {
-        return;
-      }
-    }
-
-    // New information may require visiting a new expression.
-    if (cmp.rhs_ != rhs_key && !seen.count(cmp.rhs_)) {
-      to_visit.insert(cmp.rhs_);
-      seen.insert(cmp.rhs_);
-    }
-
-    // This comparison is a stronger version of a previous constraint.
-    // Therefore, replace the old version entirely.
-    for (auto& prev_known : knowns) {
-      if (cmp.Implies(prev_known)) {
-        prev_known = cmp;
-        return;
-      }
-    }
-
-    // Neither a superset nor a subset of previously known
-    // constraints, must be tracked separately.
-    knowns.push_back(cmp);
-  };
-
-  // Initialize the search based on any known (in)equalities that use
-  // the LHS of the comparison.
-  for (const auto& known : knowns_) {
-    if (auto normalized = known.WithLHS(lhs_key)) {
-      declare_known(normalized.value());
-    }
-  }
-  for (const auto& known : scoped_knowns_) {
-    if (auto normalized = known.WithLHS(lhs_key)) {
-      declare_known(normalized.value());
-    }
-  }
-
-  // Walk through the space of all comparisons that can be made with
-  // LHS.
-  while (to_visit.size()) {
-    Key middle_key = *to_visit.begin();
-    to_visit.erase(to_visit.begin());
-
-    std::vector<Comparison>& prev_knowns_using_middle = 
compared_to_lhs.at(middle_key);
-    TVM_FFI_ICHECK(compared_to_lhs.count(middle_key));
-
-    std::vector<Comparison> new_knowns_using_lhs;
-
-    auto attempt_transitive = [&](Comparison cmp) {
-      TVM_FFI_ICHECK(cmp.IsNormalized());
-
-      Key right_key = cmp.rhs_;
-
-      if (right_key == lhs_key) {
-        return;
-      }
-
-      for (const auto& prev : prev_knowns_using_middle) {
-        CompareResult new_result = CompareResult::kUnknown;
-        auto new_offset = (ffi::BigInt(prev.offset_) + 
cmp.offset_).as<int64_t>();
-        if (!new_offset.has_value()) continue;
-
-        if (prev.result_ == CompareResult::kEQ) {
-          // x == y + c1 && y OP z + c2, x OP z + (c1 + c2)
-          new_result = cmp.result_;
-        } else if (cmp.result_ == CompareResult::kEQ) {
-          // x OP y + c1 && y == z + c2, x OP z + (c1 + c2)
-          new_result = prev.result_;
-        } else if (prev.result_ == cmp.result_ &&
-                   (prev.result_ == CompareResult::kLE || prev.result_ == 
CompareResult::kGE)) {
-          // x <= y + c1 && y <= z + c2, x <= z + (c1 + c2)
-          // x >= y + c1 && y >= z + c2, x >= z + (c1 + c2)
-          //
-          // This condition is much simpler to write than the
-          // equivalent handling of < or of >, which is why the
-          // inequalities are normalized to <= and to >=.  See
-          // `TransitiveComparisonAnalyzer::Impl::Comparison::Comparison`
-          // for further details.
-          new_result = prev.result_;
-        }
-
-        if (new_result != CompareResult::kUnknown) {
-          Comparison new_known(lhs_key, right_key, *new_offset, new_result);
-          new_knowns_using_lhs.push_back(new_known);
-        }
-      }
-    };
-
-    // Attempt to prove a new comparison using one of the original
-    // known comparisons.  We want to find a known such that
-    // `(LHS OP1 middle) && (middle OP2 right)` can be simplified
-    // into `(LHS OP3 right)`.
-    //
-    // Note: The right side is this step is not necessarily the RHS of
-    // the comparison we're trying to prove, as we may need to find
-    // intermediate comparisons first.  For example, if we know that
-    // `a<=b`, `b<=c`, and `c<=d`, and we wish to prove that `a<=d`,
-    // we must first combine `a<=b` and `b<=c` into `a<=c`.  During
-    // this first step, `b` is the "middle" and `c` is the "right".
-    // The next step can then combind `a<=c` and `c<=d` into `a<=d`.
-    for (const auto& known : knowns_) {
-      if (auto cmp = known.WithLHS(middle_key)) {
-        attempt_transitive(cmp.value());
-      }
-    }
-
-    for (const auto& known : scoped_knowns_) {
-      if (auto cmp = known.WithLHS(middle_key)) {
-        attempt_transitive(cmp.value());
-      }
-    }
-
-    // Collect together all new knowns, marking new nodes for visiting
-    // as needed.
-    for (const auto& new_known : new_knowns_using_lhs) {
-      declare_known(new_known);
-    }
-  }
-
-  if (auto it = compared_to_lhs.find(rhs_key); it != compared_to_lhs.end()) {
-    return it->second;
-  } else {
-    // There are known comparisons involving the LHS and the RHS, but
-    // no path that connects the two expressions.
-    return {};
-  }
-}
-
-CompareResult TransitiveComparisonAnalyzer::Impl::MergeComparisons(
+CompareResult KnownComparisonAnalyzer::Impl::MergeComparisons(
     const std::vector<Comparison>& lhs_to_rhs, int64_t offset) const {
   // Just because we found a comparison involving LHS and RHS doesn't
   // mean that it's useful.  e.g. Knowing that `x < y` doesn't let us
diff --git a/src/sym/rewrite_simplify.cc b/src/sym/rewrite_simplify.cc
index fa59082a4f..23e225a501 100644
--- a/src/sym/rewrite_simplify.cc
+++ b/src/sym/rewrite_simplify.cc
@@ -36,7 +36,6 @@
 #include <tuple>
 #include <utility>
 
-#include "conjunctive_normal_form.h"
 #include "const_fold.h"
 #include "constraint_extract.h"
 #include "pattern_match.h"
@@ -168,9 +167,6 @@ CompareResult RewriteSimplifier::Impl::TryCompare(const 
PrimExpr& x, const PrimE
   if (is_finished()) return output;
 
   output = CompareResult(output & TryCompareUsingKnownInequalities(x, y));
-  if (is_finished()) return output;
-
-  output = CompareResult(output & TryComparisonOfProductAndSum(x, y));
 
   return output;
 }
@@ -182,152 +178,7 @@ CompareResult 
RewriteSimplifier::Impl::TryCompareUsingConstIntBounds(const PrimE
 
 CompareResult RewriteSimplifier::Impl::TryCompareUsingKnownInequalities(const 
PrimExpr& x,
                                                                         const 
PrimExpr& y) {
-  bool propagate_inequalities = enabled_extensions_ & 
kTransitivelyProveInequalities;
-  return analyzer_->transitive_comparisons.TryCompare(x, y, 
propagate_inequalities);
-}
-
-CompareResult RewriteSimplifier::Impl::TryComparisonOfProductAndSum(const 
PrimExpr& x,
-                                                                    const 
PrimExpr& y) {
-  bool check_comparison_of_product_and_sum = enabled_extensions_ & 
kComparisonOfProductAndSum;
-  if (!check_comparison_of_product_and_sum) {
-    return CompareResult::kUnknown;
-  }
-
-  auto opt_special_case =
-      [&]() -> std::optional<std::tuple<PrimExpr, PrimExpr, PrimExpr, 
PrimExpr>> {
-    // Match expressions of the form `(A+B)*C - (A*B)*D`.  Depending on
-    // previous simplifications, the exact form of the expression may vary.
-    PVar<PrimExpr> A, B, C, D;
-
-    // diff is `(A+B)*C - (A*B)*D`.
-    PrimExpr difference = x - y;
-    PrimExpr diff = Mutate(difference).ValueOrUnchanged(difference);
-
-    if (PMatchesOneOf{
-            (A + B) * C + (A * B) * D,
-            (A + B) * C + (B * A) * D,
-            (A * B) * D + (A + B) * C,
-            (B * A) * D + (A + B) * C,
-        }
-            .Match(diff)) {
-      return std::tuple{A.Eval(), B.Eval(), C.Eval(), -D.Eval()};
-    } else if (PMatchesOneOf{
-                   (A + B) * C + (A * B),
-                   (A + B) * C + (B * A),
-                   (A * B) + (A + B) * C,
-                   (B * A) + (A + B) * C,
-               }
-                   .Match(diff)) {
-      return std::tuple{A.Eval(), B.Eval(), C.Eval(), IntImm::Int32(-1)};
-    } else {
-      return std::nullopt;
-    }
-  }();
-
-  if (!opt_special_case.has_value()) {
-    return CompareResult::kUnknown;
-  }
-  auto [A, B, C, D] = *opt_special_case;
-
-  auto A_bound = analyzer_->const_int_bound(A);
-  auto B_bound = analyzer_->const_int_bound(B);
-  auto C_bound = analyzer_->const_int_bound(C);
-  auto D_bound = analyzer_->const_int_bound(D);
-
-  auto negate = [](ConstIntBound bound) {
-    return ConstIntBound(-bound->max_value, -bound->min_value);
-  };
-  auto is_negative = [](const ConstIntBound& bound) { return bound->max_value 
< 0; };
-  auto is_positive = [](const ConstIntBound& bound) { return bound->min_value 
> 0; };
-
-  // If D is negative, then we'll be providing an upper bound for
-  // `(A*B)*D`, rather than a lower bound.  To avoid code duplication,
-  // flip all the signs here, find a lower bound, then flip the sign
-  // to produce the upper bound of the original expression.
-  //
-  // Before: (A+B)*C < (A*B)*D
-  // After:  (A*B)*(-D) < (A + B)*(-C)
-  bool is_upper_bound = is_negative(D_bound);
-  if (is_upper_bound) {
-    C_bound = negate(C_bound);
-    D_bound = negate(D_bound);
-  }
-
-  // Before: (A+B)*C < (A*B)*D
-  // After:  ((-A) + (-B))*(-C) < ((-A)*(-B))*D
-  if (is_negative(C_bound)) {
-    A_bound = negate(A_bound);
-    B_bound = negate(B_bound);
-    C_bound = negate(C_bound);
-  }
-
-  bool all_terms_positive = (is_positive(A_bound) && is_positive(B_bound) && 
is_positive(C_bound) &&
-                             is_positive(D_bound));
-  if (!all_terms_positive) {
-    return CompareResult::kUnknown;
-  }
-
-  // (A + B) * C < (A * B) * D
-  // (A + B) * C / (A*B*C*D) < (A * B) * D / (A*B*C*D)
-  // 1/(A*D) + 1/(B*D) < 1/C
-  // (A*B*C*D) * ( (A+B)/(A*B*D) - 1/C )
-  // (A*B*C*D) * ( (1/A + 1/B)/D - 1/C )
-  // (A*B*C*D) * (1/(A*D) + 1/(B*D) - 1/C)
-  //
-  // The constant (A*B*C*D) is positive, and its minimum value is the
-  // product of the minimum values of A, B, C, and D.  If the reciprocal
-  // term (1/(A*D) + 1/(B*D) - 1/C) is positive, then this constant can
-  // be used to provide a lower bound on the expression.
-
-  bool reciprocal_term_is_positive = [&]() {
-    if (D_bound->max_value == ConstIntBound::kPosInf) {
-      // If D can grow without bound, the `1/(A*D)` and `1/(B*D)`
-      // terms will approach zero, at which point the `-1/C` term
-      // will determine the sign the sign.
-      return false;
-    }
-
-    if (std::min(A_bound->max_value, B_bound->max_value) * D_bound->max_value 
<=
-        C_bound->min_value) {
-      // 1/(A*D) + 1/(B*D) - 1/C is positive if 1/C < 1/(A*D) + 1/(B*D).
-      // Since each term is positive, this condition can hold if either
-      // A*D <= C or B*D <= C.
-      return true;
-    }
-    if (A_bound->max_value != ConstIntBound::kPosInf &&
-        B_bound->max_value != ConstIntBound::kPosInf) {
-      // Even if neither term is sufficient on its own, if both A and B
-      // have known upper bounds, the inequality 1/C < 1/(A*D) + 1/(B*D)
-      // may still be provable.
-      //
-      // The maximum value of the LHS is found when C is minimized.  The
-      // minimum value of the RHS is found when A, B, and D are
-      // maximized.  If the condition holds in this case, then it holds
-      // in all cases.
-      //
-      // 1/C_min < 1/(A_max * D_max) + 1/(B_max*D_max)
-      // A_max*B_max*D_max < C_min*B_max + C_min*A_max
-      // A_max*B_max*D_max < C_min*(A_max + B_max)
-      //
-      if (A_bound->max_value * B_bound->max_value * D_bound->max_value <
-          C_bound->min_value * (A_bound->max_value + B_bound->max_value)) {
-        return true;
-      }
-    }
-    return false;
-  }();
-
-  if (!reciprocal_term_is_positive) {
-    return CompareResult::kUnknown;
-  }
-
-  if (is_upper_bound) {
-    // If we flipped the sign of the original expression, flip the sign of
-    // the resulting set of possible values.
-    return CompareResult::kLT;
-  } else {
-    return CompareResult::kGT;
-  }
+  return analyzer_->known_comparisons.TryCompare(x, y);
 }
 
 // try to prove x equals val
@@ -543,12 +394,6 @@ std::function<void()> 
RewriteSimplifier::Impl::EnterConstraint(const PrimExpr& c
   return frecover;
 }
 
-void RewriteSimplifier::Impl::SetEnabledExtensions(Extension flags) { 
enabled_extensions_ = flags; }
-
-RewriteSimplifier::Extension RewriteSimplifier::Impl::GetEnabledExtensions() 
const {
-  return enabled_extensions_;
-}
-
 UnchangedOr<PrimExpr> RewriteSimplifier::Impl::Mutate_(const prim::SubNode* op,
                                                        InplaceMode 
inplace_mode) {
   PrimExpr ret =
@@ -2152,59 +1997,13 @@ PrimExpr 
RewriteSimplifier::Impl::ApplyRewriteRules(prim::Not ret, InplaceMode i
 
 UnchangedOr<PrimExpr> RewriteSimplifier::Impl::Mutate_(const prim::AndNode* op,
                                                        InplaceMode 
inplace_mode) {
-  PrimExpr ret{ffi::UnsafeInit{}};
-  // If this extension isn't enabled, just delegate out.
-  if (!(enabled_extensions_ & kApplyConstraintsToBooleanBranches)) {
-    ret = SimplifierBase::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<PrimExpr>(op));
-  } else {
-    PrimExpr a = op->a;
-    PrimExpr b = op->b;
-
-    // Alternate which branch is used as the constraint, and which is
-    // being simplified.  Because some sub-analyzers expect their
-    // constraints to already be simplified, each branch may require
-    // more than one update.  The loop condition allows each branch to
-    // be visited up to twice, but only performs the second visit if
-    // necessary.
-    size_t iterations_since_update = 0;
-    for (size_t i = 0; i < 4; i++) {
-      PrimExpr& to_update = (i % 2 == 0) ? a : b;
-      const PrimExpr& constraint = (i % 2 == 0) ? b : a;
-
-      With<ConstraintContext> context(analyzer_, constraint);
-      // Keep this iteration out of place: convergence uses same_as() to 
detect updates.
-      // An in-place change would preserve identity and could stop the loop 
early.
-      PrimExpr updated = Mutate(to_update, 
InplaceMode::kDisallow).ValueOrUnchanged(to_update);
-
-      if (!to_update.same_as(updated)) {
-        to_update = updated;
-        iterations_since_update = 0;
-      } else {
-        iterations_since_update++;
-        if (iterations_since_update >= 2) {
-          break;
-        }
-      }
-    }
-
-    // Only construct a new object if a change has been made.
-    // Otherwise, follow ExprMutator's convention of returning the
-    // original object.
-    if (a.same_as(op->a) && b.same_as(op->b)) {
-      ret = ffi::GetRef<PrimExpr>(op);
-    } else {
-      ret = prim::And(a, b);
-    }
-  }
+  PrimExpr ret =
+      SimplifierBase::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<PrimExpr>(op));
 
   op = ret.as<prim::AndNode>();
 
   if (auto const_res = TryConstFold<prim::And>(op->a, op->b)) return 
*std::move(const_res);
   if (auto match = TryMatchLiteralConstraint(ret)) return match.value();
-  if ((enabled_extensions_ & RewriteSimplifier::kConvertBooleanToAndOfOrs) &&
-      !recursively_visiting_boolean_) {
-    return SimplifyAsAndOfOrs(ret, analyzer_);
-  }
 
   // Pattern var to match any expression
   PVar<PrimExpr> x, y, z;
@@ -2303,58 +2102,12 @@ UnchangedOr<PrimExpr> 
RewriteSimplifier::Impl::Mutate_(const prim::OrNode* op,
                                                        InplaceMode 
inplace_mode) {
   PrimExpr orig = ffi::GetRef<PrimExpr>(op);
 
-  PrimExpr ret{ffi::UnsafeInit{}};
-  // If this extension isn't enabled, just delegate out.
-  if (!(enabled_extensions_ & kApplyConstraintsToBooleanBranches)) {
-    ret = SimplifierBase::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<PrimExpr>(op));
-  } else {
-    PrimExpr a = op->a;
-    PrimExpr b = op->b;
-
-    // Alternate which branch is used as the constraint, and which
-    // is being simplified.  Because some sub-analyzers expect their
-    // constraints to already be simplified, each branch may require
-    // more than update.  The loop condition allows each branch to be
-    // visited up to twice, but only if performs the second visit if
-    // necessary.
-    size_t iterations_since_update = 0;
-    for (size_t i = 0; i < 4; i++) {
-      PrimExpr& to_update = (i % 2 == 0) ? a : b;
-      const PrimExpr& constraint = (i % 2 == 0) ? b : a;
-
-      With<ConstraintContext> context(analyzer_, 
NormalizeBooleanOperators(prim::Not(constraint)));
-      // Keep this iteration out of place: convergence uses same_as() to 
detect updates.
-      // An in-place change would preserve identity and could stop the loop 
early.
-      PrimExpr updated = Mutate(to_update, 
InplaceMode::kDisallow).ValueOrUnchanged(to_update);
-
-      if (!to_update.same_as(updated)) {
-        to_update = updated;
-        iterations_since_update = 0;
-      } else {
-        iterations_since_update++;
-        if (iterations_since_update >= 2) {
-          break;
-        }
-      }
-    }
-
-    // Only construct a new object if a change has been made.
-    // Otherwise, follow ExprMutator's convention of returning the
-    // original object.
-    if (a.same_as(op->a) && b.same_as(op->b)) {
-      ret = ffi::GetRef<PrimExpr>(op);
-    } else {
-      ret = prim::Or(a, b);
-    }
-  }
+  PrimExpr ret =
+      SimplifierBase::Mutate_(op, 
inplace_mode).ValueOrUnchanged(ffi::GetRef<PrimExpr>(op));
 
   op = ret.as<prim::OrNode>();
   if (auto const_res = TryConstFold<prim::Or>(op->a, op->b)) return 
*std::move(const_res);
   if (auto match = TryMatchLiteralConstraint(ret)) return match.value();
-  if ((enabled_extensions_ & RewriteSimplifier::kConvertBooleanToAndOfOrs) &&
-      !recursively_visiting_boolean_) {
-    return SimplifyAsAndOfOrs(ret, analyzer_);
-  }
 
   // Pattern var to match any expression
   PVar<PrimExpr> x, y, z;
@@ -2632,13 +2385,6 @@ std::function<void()> 
RewriteSimplifier::EnterConstraint(const PrimExpr& constra
   return impl_->EnterConstraint(constraint, is_assume);
 }
 
-void RewriteSimplifier::SetEnabledExtensions(Extension flags) {
-  impl_->SetEnabledExtensions(flags);
-}
-RewriteSimplifier::Extension RewriteSimplifier::GetEnabledExtensions() const {
-  return impl_->GetEnabledExtensions();
-}
-
 ffi::ObjectRef RewriteSimplifier::GetStatsCounters() const { return 
impl_->GetStatsCounters(); }
 
 void RewriteSimplifier::ResetStatsCounters() { impl_->ResetStatsCounters(); }
diff --git a/src/sym/rewrite_simplify.h b/src/sym/rewrite_simplify.h
index f67ca79a7d..a374f9189e 100644
--- a/src/sym/rewrite_simplify.h
+++ b/src/sym/rewrite_simplify.h
@@ -124,16 +124,6 @@ class RewriteSimplifier::Impl : public SimplifierBase {
 
   std::function<void()> EnterConstraint(const PrimExpr& constraint, bool 
is_assume);
 
-  /*! \brief Enable an optional extension or extensions
-   *
-   * \param flags A bitwise OR of all optional extensions that should
-   * be enabled.
-   */
-  void SetEnabledExtensions(Extension flags);
-
-  /*! \brief Return the currently enabled extensions */
-  Extension GetEnabledExtensions() const;
-
   RewriteSimplifierStats GetStatsCounters() const { return 
RewriteSimplifierStats(stats_); }
 
   void ResetStatsCounters() { stats_ = {}; }
@@ -143,7 +133,6 @@ class RewriteSimplifier::Impl : public SimplifierBase {
   void CopyFrom(const Impl& other) {
     var_map_ = other.var_map_;
     literal_constraints_ = other.literal_constraints_;
-    enabled_extensions_ = other.enabled_extensions_;
     maximum_rewrite_steps_ = other.maximum_rewrite_steps_;
   }
 
@@ -168,13 +157,6 @@ class RewriteSimplifier::Impl : public SimplifierBase {
 
   std::vector<PrimExpr> literal_constraints_;
 
-  // Optionally enabled extensions
-  Extension enabled_extensions_{kNone};
-
-  /*! Whether the simplifier is current
-   */
-  bool recursively_visiting_boolean_{false};
-
   // maximum number of recursion allowed during a single pass.
   static const constexpr int64_t kMaxRecurDepth = 5;
   /*!
@@ -233,7 +215,6 @@ class RewriteSimplifier::Impl : public SimplifierBase {
  private:
   CompareResult TryCompareUsingKnownInequalities(const PrimExpr& x, const 
PrimExpr& y);
   CompareResult TryCompareUsingConstIntBounds(const PrimExpr& x, const 
PrimExpr y);
-  CompareResult TryComparisonOfProductAndSum(const PrimExpr& x, const 
PrimExpr& y);
 
   // Whether x >= val
   bool CanProveGreaterEqual(const PrimExpr& x, int64_t val) {
diff --git a/src/tirx/transform/stmt_simplify.cc 
b/src/tirx/transform/stmt_simplify.cc
index f845881e08..f80354f537 100644
--- a/src/tirx/transform/stmt_simplify.cc
+++ b/src/tirx/transform/stmt_simplify.cc
@@ -77,7 +77,6 @@ class StmtSimplifier : public StmtExprMutatorWithAnalyzer {
 };
 
 Function StmtSimplifier::Run(Function func) {
-  
analyzer_->rewrite_simplify.SetEnabledExtensions(sym::RewriteSimplifier::kNone);
   MarkTensorParamShapes(func);
   auto* n = func.CopyOnWrite();
   // Shared string literals no longer enter the primitive analyzer.  Inline 
their
diff --git a/tests/python/relax/test_transform_adjust_matmul_order.py 
b/tests/python/relax/test_transform_adjust_matmul_order.py
deleted file mode 100644
index 2fce58f328..0000000000
--- a/tests/python/relax/test_transform_adjust_matmul_order.py
+++ /dev/null
@@ -1,1034 +0,0 @@
-# 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.
-
-import inspect
-
-import numpy as np
-import pytest
-import torch
-
-import tvm
-import tvm.testing
-from tvm import relax
-from tvm.script import ir as I
-from tvm.script import relax as R
-from tvm.script import tirx as T
-
-
-class Base:
-    def test_compare(self):
-        transform = relax.transform.AdjustMatmulOrder()
-
-        if inspect.isclass(self.Expected) and issubclass(self.Expected, 
Exception):
-            with pytest.raises(self.Expected):
-                transform(self.Before)
-        else:
-            after = transform(self.Before)
-            tvm.ir.assert_structural_equal(self.Expected, after)
-
-
-class TestLHS(Base):
-    """Prefer (x*A)*B instead of x*(A*B)
-
-    LHS first - (x*A)*B:
-        ops = 1*16*2 + 1*2*32 = 96
-    RHS first - x*(A*B):
-        ops = 16*2*32 + 1*16*32 = 1536
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([16]),
-            A: R.Tensor([16, 2]),
-            B: R.Tensor([2, 32]),
-        ) -> R.Tensor([32]):
-            weight: R.Tensor([16, 32]) = R.matmul(A, B)
-            out: R.Tensor([32]) = R.matmul(x, weight)
-            return out
-
-    @I.ir_module
-    class Expected:
-        @R.function
-        def main(
-            x: R.Tensor([16]),
-            A: R.Tensor([16, 2]),
-            B: R.Tensor([2, 32]),
-        ) -> R.Tensor([32]):
-            x: R.Tensor([2]) = R.matmul(x, A)
-            x: R.Tensor([32]) = R.matmul(x, B)
-            return x
-
-
-class TestRHS(Base):
-    """Prefer A*(B*x) instead of (A*B)*x
-
-    LHS first - (A*B)*x:
-        ops = 32*2*16 + 32*16*1 = 1536
-    RHS first - A*(B*x):
-        ops = 2*16*1 + 32*2*1 = 96
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([16]),
-            A: R.Tensor([32, 2]),
-            B: R.Tensor([2, 16]),
-        ) -> R.Tensor([32]):
-            weight: R.Tensor([32, 16]) = R.matmul(A, B)
-            out: R.Tensor([32]) = R.matmul(weight, x)
-            return out
-
-    @I.ir_module
-    class Expected:
-        @R.function
-        def main(
-            x: R.Tensor([16]),
-            A: R.Tensor([32, 2]),
-            B: R.Tensor([2, 16]),
-        ) -> R.Tensor([32]):
-            x: R.Tensor([2]) = R.matmul(B, x)
-            x: R.Tensor([32]) = R.matmul(A, x)
-            return x
-
-
-class TestIdempotentLHS(Base):
-    """The transform shouldn't undo itself if re-applied"""
-
-    Before = TestLHS.Expected
-    Expected = TestLHS.Expected
-
-
-class TestIdempotentRHS(Base):
-    """The transform shouldn't undo itself if re-applied"""
-
-    Before = TestRHS.Expected
-    Expected = TestRHS.Expected
-
-
-class TestPreserveCompileTimeMatmulOnLHS(Base):
-    """Prefer x*(A*B) if (A*B) can be pre-computed
-
-    If both `A` and `B` are known at compile-time, they can be lifted
-    out and computed at compile-time.  Therefore, optimization should
-    avoid breaking apart the `(A*B)` expression.
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([16]),
-            A: R.Tensor([32, 2]),
-            B: R.Tensor([2, 16]),
-        ) -> R.Tensor([32]):
-            R.func_attr({"num_input": 1})
-
-            weight = R.matmul(A, B)
-            out = R.matmul(weight, x)
-            return out
-
-    Expected = Before
-
-
-class TestPreserveCompileTimeMatmulOnRHS(Base):
-    """Prefer (A*B)*x if (A*B) can be pre-computed
-
-    If both `A` and `B` are known at compile-time, they can be lifted
-    out and computed at compile-time.  Therefore, optimization should
-    avoid breaking apart the `(A*B)` expression.
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([16]),
-            A: R.Tensor([16, 2]),
-            B: R.Tensor([2, 32]),
-        ) -> R.Tensor([32]):
-            R.func_attr({"num_input": 1})
-
-            weight = R.matmul(A, B)
-            out = R.matmul(x, weight)
-            return out
-
-    Expected = Before
-
-
-lora_r_before = T.dynamic("lora_r", "int64")
-lora_r_expected = T.dynamic("lora_r", "int64")
-
-
-class TestLHSDynamic(Base):
-    """Prefer (x*A)*B instead of x*(A*B)
-
-    This case appears when evaluating LoRA-tuned models with a dynamic
-    rank.
-
-    LHS first - (x*A)*B:
-        ops = 1*16*lora_r + 1*lora_r*32 = 48*lora_r
-    RHS first - x*(A*B):
-        ops = 16*lora_r*32 + 1*16*32 = 512*lora_r + 512
-
-    48*lora_r can be proved to be less than 512*lora_r + 512, so the LHS first 
is preferred.
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([16]),
-            A: R.Tensor([16, lora_r_before]),
-            B: R.Tensor([lora_r_before, 32]),
-        ) -> R.Tensor([32]):
-            weight: R.Tensor([16, 32]) = R.matmul(A, B)
-            out: R.Tensor([32]) = R.matmul(x, weight)
-            return out
-
-    @I.ir_module
-    class Expected:
-        @R.function
-        def main(
-            x: R.Tensor([16]),
-            A: R.Tensor([16, lora_r_expected]),
-            B: R.Tensor([lora_r_expected, 32]),
-        ) -> R.Tensor([32]):
-            x: R.Tensor([lora_r_expected]) = R.matmul(x, A)
-            x: R.Tensor([32]) = R.matmul(x, B)
-            return x
-
-
-lora_r_before = T.dynamic("lora_r", "int64")
-lora_r_expected = T.dynamic("lora_r", "int64")
-
-
-class TestRHSDynamic(Base):
-    """Prefer A*(B*x) instead of (A*B)*x
-
-    LHS first - (A*B)*x:
-        ops = 32*lora_r*16 + 32*16*1 = 512*lora_r + 512
-    RHS first - A*(B*x):
-        ops = lora_r*16*1 + 32*lora_r*1 = 48*lora_r
-
-    48*lora_r can be proved to be less than 512*lora_r + 512, so the RHS first 
is preferred.
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([16]),
-            A: R.Tensor([32, lora_r_before]),
-            B: R.Tensor([lora_r_before, 16]),
-        ) -> R.Tensor([32]):
-            weight: R.Tensor([32, 16]) = R.matmul(A, B)
-            out: R.Tensor([32]) = R.matmul(weight, x)
-            return out
-
-    @I.ir_module
-    class Expected:
-        @R.function
-        def main(
-            x: R.Tensor([16]),
-            A: R.Tensor([32, lora_r_expected]),
-            B: R.Tensor([lora_r_expected, 16]),
-        ) -> R.Tensor([32]):
-            x: R.Tensor([lora_r_expected]) = R.matmul(B, x)
-            x: R.Tensor([32]) = R.matmul(A, x)
-            return x
-
-
-class TestIdempotentLHSDynamic(Base):
-    """The transform shouldn't undo itself if re-applied"""
-
-    Before = TestLHSDynamic.Expected
-    Expected = TestLHSDynamic.Expected
-
-
-class TestIdempotentRHSDynamic(Base):
-    """The transform shouldn't undo itself if re-applied"""
-
-    Before = TestRHSDynamic.Expected
-    Expected = TestRHSDynamic.Expected
-
-
-batch_size_before = T.dynamic("batch_size", "int64")
-lora_r_before = T.dynamic("lora_r", "int64")
-
-
-class TestDynamicWithBatchSymbolic1(Base):
-    """When both batch_size and lora_r are symbolic and it cannot be proven 
which
-    is cheaper, LHS or RHS, maintain the existing order.
-
-    `Before` computes `x * (A * B)` with
-    `x: [batch_size, 1, 16]`, `A: [16, lora_r]`, `B: [lora_r, 32]`.
-
-    RHS first - x * (A * B):
-        16*lora_r*32 + batch_size*1*16*32 = 512*(lora_r + batch_size)
-
-    LHS first - (x * A) * B:
-        batch_size*1*16*lora_r + batch_size*1*lora_r*32 = 48*batch_size*lora_r
-
-    When `batch_size` and `lora_r` are known at compile-time:
-        - satisfy the inequality 48*batch_size*lora_r < 512*(lora_r + 
batch_size),
-          the LHS first is preferred.
-        - satisfy the inequality 512*(lora_r + batch_size) < 
48*batch_size*lora_r,
-          the RHS first is preferred.
-
-    Without bounds on `batch_size` and `lora_r`, neither side is provably 
cheaper.
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([batch_size_before, 1, 16]),
-            A: R.Tensor([16, lora_r_before]),
-            B: R.Tensor([lora_r_before, 32]),
-        ) -> R.Tensor([batch_size_before, 1, 32]):
-            weight: R.Tensor([16, 32]) = R.matmul(A, B)
-            out: R.Tensor([batch_size_before, 1, 32]) = R.matmul(x, weight)
-            return out
-
-    Expected = Before
-
-
-class TestDynamicWithBatchConcrete1LHSFirst(Base):
-    """With concrete shapes, LHS first is provably cheaper.
-
-    batch_size=4, lora_r=16:
-        LHS first: 48*4*16 = 3072
-        RHS first: 512*(16 + 4) = 10240
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([4, 1, 16]),
-            A: R.Tensor([16, 16]),
-            B: R.Tensor([16, 32]),
-        ) -> R.Tensor([4, 1, 32]):
-            weight: R.Tensor([16, 32]) = R.matmul(A, B)
-            out: R.Tensor([4, 1, 32]) = R.matmul(x, weight)
-            return out
-
-    @I.ir_module
-    class Expected:
-        @R.function
-        def main(
-            x: R.Tensor([4, 1, 16]),
-            A: R.Tensor([16, 16]),
-            B: R.Tensor([16, 32]),
-        ) -> R.Tensor([4, 1, 32]):
-            weight: R.Tensor([4, 1, 16]) = R.matmul(x, A)
-            out: R.Tensor([4, 1, 32]) = R.matmul(weight, B)
-            return out
-
-
-class TestDynamicWithBatchConcrete1RHSFirst(Base):
-    """With concrete shapes, RHS first is provably cheaper.
-
-    batch_size=64, lora_r=16:
-        LHS first: 48*64*16 = 49152
-        RHS first: 512*(16 + 64) = 40960
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([64, 1, 16]),
-            A: R.Tensor([16, 16]),
-            B: R.Tensor([16, 32]),
-        ) -> R.Tensor([64, 1, 32]):
-            weight: R.Tensor([64, 1, 16]) = R.matmul(x, A)
-            out: R.Tensor([64, 1, 32]) = R.matmul(weight, B)
-            return out
-
-    @I.ir_module
-    class Expected:
-        @R.function
-        def main(
-            x: R.Tensor([64, 1, 16]),
-            A: R.Tensor([16, 16]),
-            B: R.Tensor([16, 32]),
-        ) -> R.Tensor([64, 1, 32]):
-            weight: R.Tensor([16, 32]) = R.matmul(A, B)
-            out: R.Tensor([64, 1, 32]) = R.matmul(x, weight)
-            return out
-
-
-batch_size_before = T.dynamic("batch_size", "int64")
-lora_r_before = T.dynamic("lora_r", "int64")
-
-
-class TestDynamicWithBatchSymbolic2(Base):
-    """When both batch_size and lora_r are symbolic and it cannot be proven 
which
-    is cheaper, LHS or RHS, maintain the existing order.
-
-    `Before` computes `(A * B) * x` with
-    `A: [32, lora_r]`, `B: [lora_r, 16]`, `x: [batch_size, 16, 1]`.
-
-    LHS first - (A * B) * x:
-        32*lora_r*16 + batch_size*32*16*1 = 512*(lora_r + batch_size)
-
-    RHS first - A * (B * x):
-        batch_size*lora_r*16*1 + batch_size*32*lora_r*1 = 48*batch_size*lora_r
-
-    When `batch_size` and `lora_r` are known at compile-time:
-        - satisfy the inequality 48*batch_size*lora_r < 512*(lora_r + 
batch_size),
-          the RHS first is preferred.
-        - satisfy the inequality 512*(lora_r + batch_size) < 
48*batch_size*lora_r,
-          the LHS first is preferred.
-
-    Without bounds on `batch_size` and `lora_r`, neither side is provably 
cheaper.
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([batch_size_before, 16, 1]),
-            A: R.Tensor([32, lora_r_before]),
-            B: R.Tensor([lora_r_before, 16]),
-        ) -> R.Tensor([batch_size_before, 32, 1]):
-            weight: R.Tensor([32, 16]) = R.matmul(A, B)
-            out: R.Tensor([batch_size_before, 32, 1]) = R.matmul(weight, x)
-            return out
-
-    Expected = Before
-
-
-class TestDynamicWithBatchConcrete2RHSFirst(Base):
-    """With concrete shapes, RHS first is provably cheaper.
-
-    batch_size=4, lora_r=16:
-        RHS first: 48*4*16 = 3072
-        LHS first: 512*(16 + 4) = 10240
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([4, 16, 1]),
-            A: R.Tensor([32, 16]),
-            B: R.Tensor([16, 16]),
-        ) -> R.Tensor([4, 32, 1]):
-            weight: R.Tensor([32, 16]) = R.matmul(A, B)
-            out: R.Tensor([4, 32, 1]) = R.matmul(weight, x)
-            return out
-
-    @I.ir_module
-    class Expected:
-        @R.function
-        def main(
-            x: R.Tensor([4, 16, 1]),
-            A: R.Tensor([32, 16]),
-            B: R.Tensor([16, 16]),
-        ) -> R.Tensor([4, 32, 1]):
-            weight: R.Tensor([4, 16, 1]) = R.matmul(B, x)
-            out: R.Tensor([4, 32, 1]) = R.matmul(A, weight)
-            return out
-
-
-class TestDynamicWithBatchConcrete2LHSFirst(Base):
-    """With concrete shapes, LHS first is provably cheaper.
-
-    batch_size=64, lora_r=16:
-        RHS first: 48*64*16 = 49152
-        LHS first: 512*(16 + 64) = 40960
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([64, 16, 1]),
-            A: R.Tensor([32, 16]),
-            B: R.Tensor([16, 16]),
-        ) -> R.Tensor([64, 32, 1]):
-            weight: R.Tensor([64, 16, 1]) = R.matmul(B, x)
-            out: R.Tensor([64, 32, 1]) = R.matmul(A, weight)
-            return out
-
-    @I.ir_module
-    class Expected:
-        @R.function
-        def main(
-            x: R.Tensor([64, 16, 1]),
-            A: R.Tensor([32, 16]),
-            B: R.Tensor([16, 16]),
-        ) -> R.Tensor([64, 32, 1]):
-            weight: R.Tensor([32, 16]) = R.matmul(A, B)
-            out: R.Tensor([64, 32, 1]) = R.matmul(weight, x)
-            return out
-
-
-M_before = T.dynamic("M", "int64")
-N_before = T.dynamic("N", "int64")
-P_before = T.dynamic("P", "int64")
-Q_before = T.dynamic("Q", "int64")
-
-
-class TestNoOpForFullyDynamicOnLHS(Base):
-    """Keep existing order if no benefit can be proven
-
-    Like `TestNoOpForFullyDynamicOnRHS`, except the input has the LHS
-    computed first.
-
-    Here, it is uncertain whether a reordering would improve the
-    number of operations.
-
-    LHS first: (M+P)*N*Q
-    RHS first: (N+Q)*M*P
-
-    After simplifying, the LHS should be performed first if the
-    following inequality holds:
-
-        1/M + 1/P < 1/N + 1/Q
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            A: R.Tensor([M_before, N_before]),
-            B: R.Tensor([N_before, P_before]),
-            C: R.Tensor([P_before, Q_before]),
-        ):
-            out = R.matmul(R.matmul(A, B), C)
-            return out
-
-    Expected = Before
-
-
-M_before = T.dynamic("M", "int64")
-N_before = T.dynamic("N", "int64")
-P_before = T.dynamic("P", "int64")
-Q_before = T.dynamic("Q", "int64")
-
-
-class TestNoOpForFullyDynamicOnRHS(Base):
-    """Keep existing order if no benefit can be proven
-
-    Like `TestNoOpForFullyDynamicOnLHS`, except the input has the RHS
-    computed first.
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            A: R.Tensor([M_before, N_before]),
-            B: R.Tensor([N_before, P_before]),
-            C: R.Tensor([P_before, Q_before]),
-        ):
-            out = R.matmul(A, R.matmul(B, C))
-            return out
-
-    Expected = Before
-
-
-class TestRHSPermuteDims(Base):
-    """Prefer (x*A)*B instead of x*(A*B)
-
-    Like `TestRHS`, but the weights on the RHS are transposed.
-
-    Before: x * (BT * AT)
-        ops = 16*2*32 + 1*16*32 = 1536
-    After: (x * BT) * AT
-        ops = 1*16*2 + 1*2*32 = 96
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([16]),
-            A: R.Tensor([32, 2]),
-            B: R.Tensor([2, 16]),
-        ) -> R.Tensor([32]):
-            linear_weight: R.Tensor([32, 16]) = R.matmul(A, B)
-            matmul_weight: R.Tensor([16, 32]) = R.permute_dims(linear_weight)
-            out: R.Tensor([32]) = R.matmul(x, matmul_weight)
-            return out
-
-    @I.ir_module
-    class Expected:
-        @R.function
-        def main(
-            x: R.Tensor([16]),
-            A: R.Tensor([32, 2]),
-            B: R.Tensor([2, 16]),
-        ) -> R.Tensor([32]):
-            B_transpose = R.permute_dims(B)
-            x: R.Tensor([2]) = R.matmul(x, B_transpose)
-            A_transpose = R.permute_dims(A)
-            x: R.Tensor([32]) = R.matmul(x, A_transpose)
-            return x
-
-
-class TestRHSPermuteDimsIdentity(Base):
-    """Do not treat an explicit identity permutation as a transpose.
-
-    `TestRHSPermuteDims` above covers the real transpose case.  Here, the
-    explicit axes preserve the inner matmul's order, so reassociation must not
-    insert transposes for its operands.
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([2]),
-            A: R.Tensor([2, 1]),
-            B: R.Tensor([1, 2]),
-        ) -> R.Tensor([2]):
-            linear_weight: R.Tensor([2, 2]) = R.matmul(A, B)
-            matmul_weight: R.Tensor([2, 2]) = R.permute_dims(linear_weight, 
axes=[0, 1])
-            out: R.Tensor([2]) = R.matmul(x, matmul_weight)
-            return out
-
-    Expected = Before
-
-
-class TestRHSPermuteDimsNonMatrixAxes(Base):
-    """Do not rewrite permutations that move a batch axis."""
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([4, 1, 4]),
-            A: R.Tensor([4, 4, 1]),
-            B: R.Tensor([4, 1, 4]),
-        ) -> R.Tensor([4, 1, 4]):
-            weight: R.Tensor([4, 4, 4]) = R.matmul(A, B)
-            permuted: R.Tensor([4, 4, 4]) = R.permute_dims(weight, axes=[1, 0, 
2])
-            out: R.Tensor([4, 1, 4]) = R.matmul(x, permuted)
-            return out
-
-    Expected = Before
-
-
-class TestLHSPermuteDimsNonMatrixAxes(Base):
-    """Apply the same batch-axis guard to the left-hand pattern."""
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            A: R.Tensor([4, 4, 1]),
-            B: R.Tensor([4, 1, 4]),
-            x: R.Tensor([4, 4, 1]),
-        ) -> R.Tensor([4, 4, 1]):
-            weight: R.Tensor([4, 4, 4]) = R.matmul(A, B)
-            permuted: R.Tensor([4, 4, 4]) = R.permute_dims(weight, axes=[1, 0, 
2])
-            out: R.Tensor([4, 4, 1]) = R.matmul(permuted, x)
-            return out
-
-    Expected = Before
-
-
-lora_r_before = T.dynamic("lora_r", "int64")
-lora_r_expected = T.dynamic("lora_r", "int64")
-
-
-class TestRHSPermuteDimsDynamic(Base):
-    """Prefer (x*A)*B instead of x*(A*B)
-
-    Like `TestRHSPermuteDims`, but the weights on the RHS have a
-    dynamic shape.
-
-    Before: x * (BT * AT)
-        ops = 16*lora_r*32 + 1*16*32 = 512*lora_r + 512
-    After: (x * BT) * AT
-        ops = 1*16*lora_r + 1*lora_r*32 = 48*lora_r
-
-    48*lora_r can be proved to be less than 512*lora_r + 512, so the After is 
preferred.
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([16]),
-            A: R.Tensor([32, lora_r_before]),
-            B: R.Tensor([lora_r_before, 16]),
-        ) -> R.Tensor([32]):
-            linear_weight: R.Tensor([32, 16]) = R.matmul(A, B)
-            matmul_weight: R.Tensor([16, 32]) = R.permute_dims(linear_weight)
-            out: R.Tensor([32]) = R.matmul(x, matmul_weight)
-            return out
-
-    @I.ir_module
-    class Expected:
-        @R.function
-        def main(
-            x: R.Tensor([16]),
-            A: R.Tensor([32, lora_r_expected]),
-            B: R.Tensor([lora_r_expected, 16]),
-        ) -> R.Tensor([32]):
-            B_transpose = R.permute_dims(B)
-            x: R.Tensor([lora_r_expected]) = R.matmul(x, B_transpose)
-            A_transpose = R.permute_dims(A)
-            x: R.Tensor([32]) = R.matmul(x, A_transpose)
-            return x
-
-
-lora_r_before = T.dynamic("lora_r", "int64")
-batch_size_before = T.dynamic("batch_size", "int64")
-lora_r_expected = T.dynamic("lora_r", "int64")
-batch_size_expected = T.dynamic("batch_size", "int64")
-
-
-class TestRHSPermuteDimsWithDynamicBatch(Base):
-    """Prefer (x*A)*B instead of x*(A*B)
-
-    Like `TestRHSPermuteDims`, but both the weights on the RHS and the
-    activations on the LHS have a dynamic dimension.
-
-    Unlike most of the tests for this transform, the
-    `tir_vars_upper_bound` attribute is required.  In order to make a
-    change, `AdjustMatmulOrder` must first prove that the modified
-    execution order reduces the number of computations.
-
-        ops_left_to_right = (batch_size + lora_r)*4096*4096
-        ops_right_to_left = (4096 + 4096)*batch_size*lora_r
-
-    Without an upper bound on batch_size and`lora_r`, we cannot prove which
-    of these is the preferred execution order.
-
-    With the upper bound, TVM can determine the preferred order using
-    the following arithmetic reasoning.
-
-        (batch_size + lora_r)*4096*4096 > (4096 + 4096)*batch_size*lora_r
-        (batch_size + lora_r)*2048 > batch_size*lora_r
-        1/batch_size + 1/lora_r > 1/2048
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([batch_size_before, 4096]),
-            A: R.Tensor([4096, lora_r_before]),
-            B: R.Tensor([lora_r_before, 4096]),
-        ) -> R.Tensor([batch_size_before, 4096]):
-            R.func_attr(
-                {
-                    "tir_var_upper_bound": {"lora_r": 2048, "batch_size": 
2048},
-                }
-            )
-            linear_weight: R.Tensor([4096, 4096]) = R.matmul(A, B)
-            matmul_weight: R.Tensor([4096, 4096]) = 
R.permute_dims(linear_weight)
-            out: R.Tensor([batch_size_before, 4096]) = R.matmul(x, 
matmul_weight)
-            return out
-
-    @I.ir_module
-    class Expected:
-        @R.function
-        def main(
-            x: R.Tensor([batch_size_expected, 4096]),
-            A: R.Tensor([4096, lora_r_expected]),
-            B: R.Tensor([lora_r_expected, 4096]),
-        ) -> R.Tensor([batch_size_expected, 4096]):
-            R.func_attr(
-                {
-                    "tir_var_upper_bound": {"lora_r": 2048, "batch_size": 
2048},
-                }
-            )
-            B_transpose = R.permute_dims(B)
-            x: R.Tensor([batch_size_expected, lora_r_expected]) = R.matmul(x, 
B_transpose)
-            A_transpose = R.permute_dims(A)
-            x: R.Tensor([batch_size_expected, 4096]) = R.matmul(x, A_transpose)
-            return x
-
-
-lora_r_before = T.dynamic("lora_r", "int64")
-lora_r_expected = T.dynamic("lora_r", "int64")
-
-
-class TestRHSPermuteDimsDynamicWithSquareMatrix(Base):
-    """Prefer (x*A)*B instead of x*(A*B)
-
-    Like `TestRHSPermuteDims`, but the weights on the RHS have a
-    dynamic shape.
-
-    Before: x * (BT * AT)
-        ops = 32*lora_r*32 + 1*32*32 = 1024*lora_r + 1024
-    After: (x * BT) * AT
-        ops = 1*32*lora_r + 1*lora_r*32 = 64*lora_r
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            x: R.Tensor([32]),
-            A: R.Tensor([32, lora_r_before]),
-            B: R.Tensor([lora_r_before, 32]),
-        ) -> R.Tensor([32]):
-            linear_weight: R.Tensor([32, 32]) = R.matmul(A, B)
-            matmul_weight: R.Tensor([32, 32]) = R.permute_dims(linear_weight)
-            out: R.Tensor([32]) = R.matmul(x, matmul_weight)
-            return out
-
-    @I.ir_module
-    class Expected:
-        @R.function
-        def main(
-            x: R.Tensor([32]),
-            A: R.Tensor([32, lora_r_expected]),
-            B: R.Tensor([lora_r_expected, 32]),
-        ) -> R.Tensor([32]):
-            B_transpose = R.permute_dims(B)
-            x: R.Tensor([lora_r_expected]) = R.matmul(x, B_transpose)
-            A_transpose = R.permute_dims(A)
-            x: R.Tensor([32]) = R.matmul(x, A_transpose)
-            return x
-
-
-class TestBatchedBroadcastPreferLHSFirst(Base):
-    """Use broadcasted batch prefix per matmul, not independent prefix 
products.
-
-    Example with broadcast batch axes: A:[2,1,1], B:[2,1,2], C:[2,2,3].
-
-    LHS first: (A * B) * C
-        ops = 2*1*1*2 + 2*1*2*3 = 16
-    RHS first: A * (B * C)
-        ops = 2*1*2*3 + 2*1*1*3 = 18
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            A: R.Tensor([2, 1, 1]),
-            B: R.Tensor([2, 1, 2]),
-            C: R.Tensor([2, 2, 3]),
-        ) -> R.Tensor([2, 1, 3]):
-            out: R.Tensor([2, 1, 3]) = R.matmul(A, R.matmul(B, C))
-            return out
-
-    @I.ir_module
-    class Expected:
-        @R.function
-        def main(
-            A: R.Tensor([2, 1, 1]),
-            B: R.Tensor([2, 1, 2]),
-            C: R.Tensor([2, 2, 3]),
-        ) -> R.Tensor([2, 1, 3]):
-            temp: R.Tensor([2, 1, 2]) = R.matmul(A, B)
-            out: R.Tensor([2, 1, 3]) = R.matmul(temp, C)
-            return out
-
-
-class TestBatchedSharedPrefixPreferLHSFirst(Base):
-    """All operands share a nontrivial batch prefix [2, 3].
-
-    Shapes: A:[2,3,4,5], B:[2,3,5,6], C:[2,3,6,7]
-
-    LHS first:
-        ops = 6*4*5*6 + 6*4*6*7 = 1728
-    RHS first:
-        ops = 6*5*6*7 + 6*4*5*7 = 2100
-    """
-
-    @I.ir_module
-    class Before:
-        @R.function
-        def main(
-            A: R.Tensor([2, 3, 4, 5]),
-            B: R.Tensor([2, 3, 5, 6]),
-            C: R.Tensor([2, 3, 6, 7]),
-        ) -> R.Tensor([2, 3, 4, 7]):
-            out: R.Tensor([2, 3, 4, 7]) = R.matmul(A, R.matmul(B, C))
-            return out
-
-    @I.ir_module
-    class Expected:
-        @R.function
-        def main(
-            A: R.Tensor([2, 3, 4, 5]),
-            B: R.Tensor([2, 3, 5, 6]),
-            C: R.Tensor([2, 3, 6, 7]),
-        ) -> R.Tensor([2, 3, 4, 7]):
-            temp: R.Tensor([2, 3, 4, 6]) = R.matmul(A, B)
-            out: R.Tensor([2, 3, 4, 7]) = R.matmul(temp, C)
-            return out
-
-
[email protected]("lhs_first", [False, True])
[email protected]("compile_time,transpose", [(False, False), (False, 
True), (True, False)])
[email protected]("out_dtype", [None, "float32"])
-def test_preserve_outer_out_dtype(lhs_first, compile_time, transpose, 
out_dtype):
-    # Compile-time grouping should override the cheaper evaluation order.
-    shapes = [(2, 20), (20, 2), (2, 10)]
-    if lhs_first == compile_time:
-        shapes = [(10, 2), (2, 20), (20, 2)]
-    inner_indices = (1, 2) if lhs_first else (0, 1)
-    if transpose:
-        for i in inner_indices:
-            shapes[i] = shapes[i][::-1]
-
-    def build_module(expected):
-        bb = relax.BlockBuilder()
-        a, b, c = [
-            relax.Var(name, relax.TensorType(shape, "float16"))
-            for name, shape in zip("abc", shapes)
-        ]
-        params = [c, a, b] if lhs_first else [a, b, c]
-        attrs = {"num_input": 1} if compile_time else None
-        with bb.function("main", params, attrs=attrs):
-            with bb.dataflow():
-                operands = [a, b, c]
-                if expected and transpose:
-                    for i in inner_indices:
-                        operands[i] = relax.op.permute_dims(operands[i])
-                a, b, c = operands
-                if expected:
-                    if lhs_first:
-                        out = relax.op.matmul(relax.op.matmul(a, b), c, 
out_dtype=out_dtype)
-                    else:
-                        out = relax.op.matmul(a, relax.op.matmul(b, c), 
out_dtype=out_dtype)
-                else:
-                    x, y = (b, c) if lhs_first else (a, b)
-                    inner = bb.emit(relax.op.matmul(y, x) if transpose else 
relax.op.matmul(x, y))
-                    if transpose:
-                        inner = bb.emit(relax.op.permute_dims(inner))
-                    out = (
-                        relax.op.matmul(a, inner, out_dtype=out_dtype)
-                        if lhs_first
-                        else relax.op.matmul(inner, c, out_dtype=out_dtype)
-                    )
-                output = bb.emit_output(out)
-            bb.emit_func_output(output)
-        return bb.finalize()
-
-    before = build_module(expected=False)
-    expected = build_module(expected=True)
-    transform = relax.transform.AdjustMatmulOrder()
-    after = transform(before)
-    assert after["main"].ret_ty.dtype == (out_dtype or "float16")
-    tvm.ir.assert_structural_equal(after, expected)
-    tvm.ir.assert_structural_equal(transform(after), after)
-
-
-class TestAdjustMatmulOrderAttentionBlock:
-    """AdjustMatmulOrder preserves numerics on a batched attention block.
-
-    Covers ND `permute_dims` (swap last two axes) inside `matmul(q, kt)`,
-    regression for issue #19576.
-    """
-
-    def _build_attention_module(self, batch, seq, dim):
-        """Minimal batched attention block exercising ND permute_dims + 
matmul."""
-        bb = relax.BlockBuilder()
-        x = relax.Var("x", relax.TensorType((batch, seq, dim), "float32"))
-        wq = relax.Var("wq", relax.TensorType((dim, dim), "float32"))
-        wk = relax.Var("wk", relax.TensorType((dim, dim), "float32"))
-        wv = relax.Var("wv", relax.TensorType((dim, dim), "float32"))
-        wo = relax.Var("wo", relax.TensorType((dim, dim), "float32"))
-        with bb.function("main", [x, wq, wk, wv, wo]):
-            with bb.dataflow():
-                q = bb.emit(relax.op.matmul(x, wq))
-                k = bb.emit(relax.op.matmul(x, wk))
-                v = bb.emit(relax.op.matmul(x, wv))
-                kt = bb.emit(relax.op.permute_dims(k, axes=[0, 2, 1]))
-                scores = bb.emit(relax.op.matmul(q, kt))
-                scale = bb.emit(relax.const(1.0 / np.sqrt(dim), "float32"))
-                scores = bb.emit(relax.op.multiply(scores, scale))
-                attn = bb.emit(relax.op.nn.softmax(scores, axis=-1))
-                out = bb.emit(relax.op.matmul(attn, v))
-                proj = bb.emit_output(relax.op.matmul(out, wo))
-            bb.emit_func_output(proj)
-        return bb.finalize()
-
-    def _run_relax_main(self, mod, inputs):
-        exe = relax.build(mod, target="llvm")
-        vm = relax.VirtualMachine(exe, device=tvm.cpu())
-        args = [tvm.runtime.tensor(arr, device=tvm.cpu()) for arr in inputs]
-        return vm["main"](*args).numpy()
-
-    def _torch_attention_ref(self, x_np, w_np, dim):
-        x = torch.from_numpy(x_np)
-        w = torch.from_numpy(w_np)
-        with torch.no_grad():
-            q = torch.matmul(x, w)
-            k = torch.matmul(x, w)
-            v = torch.matmul(x, w)
-            scores = torch.matmul(q, k.transpose(-2, -1))
-            scores = scores * (1.0 / np.sqrt(dim))
-            attn = torch.nn.functional.softmax(scores, dim=-1)
-            out = torch.matmul(attn, v)
-            out = torch.matmul(out, w)
-        return out.detach().numpy()
-
-    @pytest.mark.parametrize("batch,seq,dim", [(2, 16, 64)])
-    def test_attention_block_numerics(self, batch, seq, dim):
-        mod = self._build_attention_module(batch, seq, dim)
-        mod_opt = relax.transform.AdjustMatmulOrder()(mod)
-
-        x_np = np.random.randn(batch, seq, dim).astype("float32")
-        w_np = np.random.randn(dim, dim).astype("float32")
-        inputs = [x_np, w_np, w_np, w_np, w_np]
-
-        ref = self._torch_attention_ref(x_np, w_np, dim)
-        out_before = self._run_relax_main(mod, inputs)
-        out_after = self._run_relax_main(mod_opt, inputs)
-
-        tvm.testing.assert_allclose(out_before, ref, rtol=1e-3, atol=1e-3)
-        tvm.testing.assert_allclose(out_after, ref, rtol=1e-3, atol=1e-3)
-        tvm.testing.assert_allclose(out_before, out_after, rtol=1e-5, 
atol=1e-5)
-
-    def test_identity_permute_dims_numerics(self):
-        bb = relax.BlockBuilder()
-        x = relax.Var("x", relax.TensorType((2,), "int32"))
-        A = relax.Var("A", relax.TensorType((2, 1), "int32"))
-        B = relax.Var("B", relax.TensorType((1, 2), "int32"))
-        with bb.function("main", [x, A, B]):
-            with bb.dataflow():
-                linear_weight = bb.emit(relax.op.matmul(A, B))
-                identity_weight = bb.emit(relax.op.permute_dims(linear_weight, 
axes=[0, 1]))
-                out = bb.emit_output(relax.op.matmul(x, identity_weight))
-            bb.emit_func_output(out)
-        mod = bb.finalize()
-        mod_opt = relax.transform.AdjustMatmulOrder()(mod)
-
-        inputs = [
-            np.array([-1, 3], dtype="int32"),
-            np.array([[2], [-4]], dtype="int32"),
-            np.array([[1, -3]], dtype="int32"),
-        ]
-        expected = np.array([-14, 42], dtype="int32")
-
-        out_before = self._run_relax_main(mod, inputs)
-        out_after = self._run_relax_main(mod_opt, inputs)
-
-        np.testing.assert_array_equal(out_before, expected)
-        np.testing.assert_array_equal(out_after, expected)
-
-
-if __name__ == "__main__":
-    tvm.testing.main()
diff --git a/tests/python/sym/test_sym_analyzer_object.py 
b/tests/python/sym/test_sym_analyzer_object.py
index d74fd81ecd..dd37a3ebd1 100644
--- a/tests/python/sym/test_sym_analyzer_object.py
+++ b/tests/python/sym/test_sym_analyzer_object.py
@@ -21,7 +21,7 @@ import tvm
 import tvm.testing
 from tvm import tirx
 from tvm.runtime import Object
-from tvm.sym.analyzer import CompareResult, Extension
+from tvm.sym.analyzer import CompareResult
 
 
 def test_analyzer_is_ffi_object_with_persistent_state():
@@ -146,7 +146,7 @@ def test_analyzer_object_set_maximum_rewrite_steps():
     relaxed.rewrite_simplify(expr)
 
 
-def test_analyzer_object_try_compare_transitive():
+def test_analyzer_object_try_compare_direct():
     analyzer = tvm.sym.Analyzer()
     x = tirx.Var("x", "int32")
     y = tirx.Var("y", "int32")
@@ -158,21 +158,8 @@ def test_analyzer_object_try_compare_transitive():
         with analyzer.constraint_scope(y < z):
             # Direct known comparison.
             assert analyzer.try_compare(x, y) == CompareResult.LT
-            # Transitive chain x < y < z is found only when propagation is 
enabled.
-            assert analyzer.try_compare(x, z) == CompareResult.LT
-            assert analyzer.try_compare(x, z, propagate_inequalities=False) == 
CompareResult.UNKNOWN
-
-
-def test_analyzer_object_enabled_extensions_round_trip():
-    analyzer = tvm.sym.Analyzer()
-
-    assert analyzer.enabled_extensions == Extension.NoExtensions
-
-    analyzer.enabled_extensions = Extension.ComparisonOfProductAndSum
-    assert analyzer.enabled_extensions == Extension.ComparisonOfProductAndSum
-
-    analyzer.enabled_extensions = Extension.NoExtensions
-    assert analyzer.enabled_extensions == Extension.NoExtensions
+            # No direct comparison connects x and z.
+            assert analyzer.try_compare(x, z) == CompareResult.UNKNOWN
 
 
 def test_analyzer_object_rewrite_simplify_stats():
@@ -238,7 +225,6 @@ def test_analyzer_object_clone_copies_every_sub_analyzer():
     analyzer.update(x, tvm.sym.ModularSet(4, 0))
     analyzer.bind(w, tirx.const(4, "int64"))
     analyzer.update(v, tvm.sym.IntervalSet(2, 9))
-    analyzer.enabled_extensions = Extension.ComparisonOfProductAndSum
 
     clone = analyzer.clone()
 
@@ -246,7 +232,6 @@ def test_analyzer_object_clone_copies_every_sub_analyzer():
     assert clone.modular_set(x).coeff == 4
     tvm.ir.assert_structural_equal(clone.simplify(w + 1), tirx.const(5, 
"int64"))
     assert clone.int_set(v).max_value.value == 9
-    assert clone.enabled_extensions == Extension.ComparisonOfProductAndSum
     assert clone.try_compare(x, tirx.const(0, "int64")) == CompareResult.GE
 
     t = tirx.Var("t", "int64")
@@ -254,7 +239,6 @@ def test_analyzer_object_clone_copies_every_sub_analyzer():
     clone.update(v, tvm.sym.IntervalSet(0, 3), override=True)
     clone.bind(w, tirx.const(8, "int64"), allow_override=True)
     clone.bind(t, tvm.ir.Range(0, 4))
-    clone.enabled_extensions = Extension.NoExtensions
 
     assert analyzer.modular_set(x).coeff == 4
     assert clone.modular_set(x).coeff == 8
@@ -262,8 +246,6 @@ def test_analyzer_object_clone_copies_every_sub_analyzer():
     assert clone.int_set(v).max_value.value == 3
     tvm.ir.assert_structural_equal(analyzer.simplify(w + 1), tirx.const(5, 
"int64"))
     tvm.ir.assert_structural_equal(clone.simplify(w + 1), tirx.const(9, 
"int64"))
-    assert analyzer.enabled_extensions == Extension.ComparisonOfProductAndSum
-    assert clone.enabled_extensions == Extension.NoExtensions
     assert clone.try_compare(t, tirx.const(0, "int64")) == CompareResult.GE
     assert analyzer.try_compare(t, tirx.const(0, "int64")) == 
CompareResult.UNKNOWN
 
diff --git a/tests/python/sym/test_sym_rewrite_simplify.py 
b/tests/python/sym/test_sym_rewrite_simplify.py
index b6efb06e02..ef4c2f5bf4 100644
--- a/tests/python/sym/test_sym_rewrite_simplify.py
+++ b/tests/python/sym/test_sym_rewrite_simplify.py
@@ -68,11 +68,8 @@ class TestCase:
 
 
 class BaseCompare:
-    extensions = tvm.sym.Extension.NoExtensions
-
     def test_simplify(self, test_case):
         analyzer = tvm.sym.Analyzer()
-        analyzer.enabled_extensions = self.extensions
 
         if inspect.isclass(test_case.expected) and 
issubclass(test_case.expected, Exception):
             with pytest.raises(test_case.expected):
@@ -1172,39 +1169,6 @@ class TestComparisons(BaseCompare):
     )
 
 
-class TestComparisonOfProductAndSum(BaseCompare):
-    extensions = tvm.sym.Extension.ComparisonOfProductAndSum
-
-    x, y, z = tvm.tirx.Var("x", "int32"), tvm.tirx.Var("y", "int32"), 
tvm.tirx.Var("z", "int32")
-
-    test_case = tvm.testing.parameter(
-        # Special inequality cases
-        TestCase(
-            x * y < (x + y) * 2048,
-            tvm.tirx.const(1, "bool"),
-            [x > 0, y > 0, x < 2048],
-        ),
-        TestCase(
-            x * y < (x + y) * 2048,
-            tvm.tirx.const(1, "bool"),
-            [x > 0, y > 0, x < 4096, y < 4096],
-        ),
-        TestCase(
-            # Both sides are divisible by 8192
-            x * y * 8192 < (y + x) * 16777216,
-            tvm.tirx.const(1, "bool"),
-            [x > 0, y > 0, x < 4096, y < 4096],
-        ),
-        TestCase(
-            # The two sides have co-prime factors, but the bounds are
-            # still sufficient to prove the inequality.
-            x * y * 59 < (y + x) * 176128,
-            tvm.tirx.const(1, "bool"),
-            [x > 0, y > 0, x < 4096, y < 4096],
-        ),
-    )
-
-
 class TestLogical(BaseCompare):
     x, y, z = tvm.tirx.Var("x", "int32"), tvm.tirx.Var("y", "int32"), 
tvm.tirx.Var("z", "int32")
 
diff --git a/tests/python/tirx-transform/test_tir_transform_simplify.py 
b/tests/python/tirx-transform/test_tir_transform_simplify.py
index d94384377b..83737ad91e 100644
--- a/tests/python/tirx-transform/test_tir_transform_simplify.py
+++ b/tests/python/tirx-transform/test_tir_transform_simplify.py
@@ -614,31 +614,6 @@ def test_left_shift_by_negative_value():
     tvm.ir.assert_structural_equal(after, expected)
 
 
-def test_suppress_transitively_provable_condition():
-    @T.function(private=True)
-    def before(A: T.Tensor(1, "bool"), i: T.int32, j: T.int32, k: T.int32):
-        if i < j and j < k:
-            A[0] = i < k
-
-    expected = before
-
-    after = _apply_simplify(before)
-    tvm.ir.assert_structural_equal(after, expected)
-
-
-def test_suppress_rewrite_as_and_of_ors():
-    """Only rewrite into AND of OR when allowed"""
-
-    @T.function(private=True)
-    def before(A: T.Tensor(3, "bool")):
-        T.evaluate(A[0] or (A[1] and A[2]))
-
-    expected = before
-
-    after = _apply_simplify(before)
-    tvm.ir.assert_structural_equal(after, expected)
-
-
 def test_conditional_floor_mod():
     """A regression test for negative floormod denominator
 

Reply via email to