class_docstr: Associative operations, can separate noncommutative and
  commutative parts.
  
  (a op b) op c == a op (b op c) == a op b op c.
  
  Base class for Add and Mul.
  
  This is an abstract base class, concrete derived classes must define
  the attribute `identity`.
  
  .. deprecated:: 1.7
  
     Using arguments that aren't subclasses of :class:`~.Expr` in core
     operators (:class:`~.Mul`, :class:`~.Add`, and :class:`~.Pow`) is
     deprecated. See :ref:`non-expr-args-deprecated` for details.
  
  Parameters
  ==========
  
  *args :
      Arguments which are operated
  
  evaluate : bool, optional
      Evaluate the operation. If not passed, refer to ``global_parameters.evaluate``.
docstr: Matches Add/Mul "pattern" to an expression "expr".
  
  repl_dict ... a dictionary of (wild: expression) pairs, that get
                returned with the results
  
  This function is the main workhorse for Add/Mul.
  
  Examples
  ========
  
  >>> from sympy import symbols, Wild, sin
  >>> a = Wild("a")
  >>> b = Wild("b")
  >>> c = Wild("c")
  >>> x, y, z = symbols("x y z")
  >>> (a+sin(b)*c)._matches_commutative(x+sin(y)*z)
  {a_: x, b_: y, c_: z}
  
  In the example above, "a+sin(b)*c" is the pattern, and "x+sin(y)*z" is
  the expression.
  
  The repl_dict contains parts that were already matched. For example
  here:
  
  >>> (x+sin(b)*c)._matches_commutative(x+sin(y)*z, repl_dict={a: x})
  {a_: x, b_: y, c_: z}
  
  the only function of the repl_dict is to return it in the
  result, e.g. if you omit it:
  
  >>> (x+sin(b)*c)._matches_commutative(x+sin(y)*z)
  {b_: y, c_: z}
  
  the "a: x" is not returned in the result, but otherwise it is
  equivalent.
docstr_tok: ["Matches","Add\/Mul","``","pattern","''","to","an","expression","``","expr","''",".","repl_dict","...","a","dictionary","of","(","wild",":","expression",")","pairs",",","that","get","returned","with","the","results","This","function","is","the","main","workhorse","for","Add\/Mul",".","Examples","========",">",">",">","from","sympy","import","symbols",",","Wild",",","sin",">",">",">","a","=","Wild","(","``","a","''",")",">",">",">","b","=","Wild","(","``","b","''",")",">",">",">","c","=","Wild","(","``","c","''",")",">",">",">","x",",","y",",","z","=","symbols","(","``","x","y","z","''",")",">",">",">","(","a+sin","(","b",")","*","c",")","._matches_commutative","(","x+sin","(","y",")","*","z",")","{","a_",":","x",",","b_",":","y",",","c_",":","z","}","In","the","example","above",",","``","a+sin","(","b",")","*","c","''","is","the","pattern",",","and","``","x+sin","(","y",")","*","z","''","is","the","expression",".","The","repl_dict","contains","parts","that","were","already","matched",".","For","example","here",":",">",">",">","(","x+sin","(","b",")","*","c",")","._matches_commutative","(","x+sin","(","y",")","*","z",",","repl_dict=","{","a",":","x","}",")","{","a_",":","x",",","b_",":","y",",","c_",":","z","}","the","only","function","of","the","repl_dict","is","to","return","it","in","the","result",",","e.g",".","if","you","omit","it",":",">",">",">","(","x+sin","(","b",")","*","c",")","._matches_commutative","(","x+sin","(","y",")","*","z",")","{","b_",":","y",",","c_",":","z","}","the","``","a",":","x","''","is","not","returned","in","the","result",",","but","otherwise","it","is","equivalent","."]
code: def _matches_commutative(self, expr, repl_dict=None, old=False):
      """
      Matches Add/Mul "pattern" to an expression "expr".
  
      repl_dict ... a dictionary of (wild: expression) pairs, that get
                    returned with the results
  
      This function is the main workhorse for Add/Mul.
  
      Examples
      ========
  
      >>> from sympy import symbols, Wild, sin
      >>> a = Wild("a")
      >>> b = Wild("b")
      >>> c = Wild("c")
      >>> x, y, z = symbols("x y z")
      >>> (a+sin(b)*c)._matches_commutative(x+sin(y)*z)
      {a_: x, b_: y, c_: z}
  
      In the example above, "a+sin(b)*c" is the pattern, and "x+sin(y)*z" is
      the expression.
  
      The repl_dict contains parts that were already matched. For example
      here:
  
      >>> (x+sin(b)*c)._matches_commutative(x+sin(y)*z, repl_dict={a: x})
      {a_: x, b_: y, c_: z}
  
      the only function of the repl_dict is to return it in the
      result, e.g. if you omit it:
  
      >>> (x+sin(b)*c)._matches_commutative(x+sin(y)*z)
      {b_: y, c_: z}
  
      the "a: x" is not returned in the result, but otherwise it is
      equivalent.
  
      """
      from .function import _coeff_isneg
  
      # make sure expr is Expr if pattern is Expr
      from .expr import Expr
  
      if isinstance(self, Expr) and not isinstance(expr, Expr):
          return None
  
      if repl_dict is None:
          repl_dict = {}
  
      # handle simple patterns
      if self == expr:
          return repl_dict
  
      d = self._matches_simple(expr, repl_dict)
      if d is not None:
          return d
  
      # eliminate exact part from pattern: (2+a+w1+w2).matches(expr) -> (w1+w2).matches(expr-a-2)
      from .function import WildFunction
      from .symbol import Wild
  
      wild_part, exact_part = sift(
          self.args,
          lambda p: p.has(Wild, WildFunction) and not expr.has(p),
          binary=True,
      )
      if not exact_part:
          wild_part = list(ordered(wild_part))
          if self.is_Add:
              # in addition to normal ordered keys, impose
              # sorting on Muls with leading Number to put
              # them in order
              wild_part = sorted(
                  wild_part,
                  key=lambda x: x.args[0]
                  if x.is_Mul and x.args[0].is_Number
                  else 0,
              )
      else:
          exact = self._new_rawargs(*exact_part)
          free = expr.free_symbols
          if free and (exact.free_symbols - free):
              # there are symbols in the exact part that are not
              # in the expr; but if there are no free symbols, let
              # the matching continue
              return None
          newexpr = self._combine_inverse(expr, exact)
          if not old and (expr.is_Add or expr.is_Mul):
              check = newexpr
              if _coeff_isneg(check):
                  check = -check
              if check.count_ops() > expr.count_ops():
                  return None
          newpattern = self._new_rawargs(*wild_part)
          return newpattern.matches(newexpr, repl_dict)
  
      # now to real work ;)
      i = 0
      saw = set()
      while expr not in saw:
          saw.add(expr)
          args = tuple(ordered(self.make_args(expr)))
          if self.is_Add and expr.is_Add:
              # in addition to normal ordered keys, impose
              # sorting on Muls with leading Number to put
              # them in order
              args = tuple(
                  sorted(
                      args,
                      key=lambda x: x.args[0]
                      if x.is_Mul and x.args[0].is_Number
                      else 0,
                  )
              )
          expr_list = (self.identity,) + args
          for last_op in reversed(expr_list):
              for w in reversed(wild_part):
                  d1 = w.matches(last_op, repl_dict)
                  if d1 is not None:
                      d2 = self.xreplace(d1).matches(expr, d1)
                      if d2 is not None:
                          return d2
  
          if i == 0:
              if self.is_Mul:
                  # make e**i look like Mul
                  if expr.is_Pow and expr.exp.is_Integer:
                      from .mul import Mul
  
                      if expr.exp > 0:
                          expr = Mul(
                              *[expr.base, expr.base ** (expr.exp - 1)],
                              evaluate=False,
                          )
                      else:
                          expr = Mul(
                              *[
                                  1 / expr.base,
                                  expr.base ** (expr.exp + 1),
                              ],
                              evaluate=False,
                          )
                      i += 1
                      continue
  
              elif self.is_Add:
                  # make i*e look like Add
                  c, e = expr.as_coeff_Mul()
                  if abs(c) > 1:
                      from .add import Add
  
                      if c > 0:
                          expr = Add(*[e, (c - 1) * e], evaluate=False)
                      else:
                          expr = Add(*[-e, (c + 1) * e], evaluate=False)
                      i += 1
                      continue
  
                  # try collection on non-Wild symbols
                  from sympy.simplify.radsimp import collect
  
                  was = expr
                  did = set()
                  for w in reversed(wild_part):
                      c, w = w.as_coeff_mul(Wild)
                      free = c.free_symbols - did
                      if free:
                          did.update(free)
                          expr = collect(expr, free)
                  if expr != was:
                      i += 0
                      continue
  
              break  # if we didn't continue, there is nothing more to do
  
      return
  
imports: from __future__ import annotations
  from operator import attrgetter
  from collections import defaultdict
  from sympy.utilities.exceptions import sympy_deprecation_warning
  from .sympify import _sympify, sympify
  from .basic import Basic
  from .cache import cacheit
  from .sorting import ordered
  from .logic import fuzzy_and
  from .parameters import global_parameters
  from sympy.utilities.iterables import sift
  from sympy.multipledispatch.dispatcher import Dispatcher, ambiguity_register_error_ignore_dup, str_signature, RaiseNotImplementedError
label_desc: Use image node_id 5 for calling the AssocOp obj's underlying member method code with example usage: obj._matches_commutative(expr, repl_dict, old) and returns: None, repl_dict, d, newpattern, None, None, d2