code: def _minpoly_groebner(ex, x, cls):
      """
      Computes the minimal polynomial of an algebraic number
      using Groebner bases
  
      Examples
      ========
  
      >>> from sympy import minimal_polynomial, sqrt, Rational
      >>> from sympy.abc import x
      >>> minimal_polynomial(sqrt(2) + 3*Rational(1, 3), x, compose=False)
      x**2 - 2*x - 1
  
      """
  
      generator = numbered_symbols("a", cls=Dummy)
      mapping, symbols = {}, {}
  
      def update_mapping(ex, exp, base=None):
          a = next(generator)
          symbols[ex] = a
  
          if base is not None:
              mapping[ex] = a**exp + base
          else:
              mapping[ex] = exp.as_expr(a)
  
          return a
  
      def bottom_up_scan(ex):
          """
          Transform a given algebraic expression *ex* into a multivariate
          polynomial, by introducing fresh variables with defining equations.
  
          Explanation
          ===========
  
          The critical elements of the algebraic expression *ex* are root
          extractions, instances of :py:class:`~.AlgebraicNumber`, and negative
          powers.
  
          When we encounter a root extraction or an :py:class:`~.AlgebraicNumber`
          we replace this expression with a fresh variable ``a_i``, and record
          the defining polynomial for ``a_i``. For example, if ``a_0**(1/3)``
          occurs, we will replace it with ``a_1``, and record the new defining
          polynomial ``a_1**3 - a_0``.
  
          When we encounter a negative power we transform it into a positive
          power by algebraically inverting the base. This means computing the
          minimal polynomial in ``x`` for the base, inverting ``x`` modulo this
          poly (which generates a new polynomial) and then substituting the
          original base expression for ``x`` in this last polynomial.
  
          We return the transformed expression, and we record the defining
          equations for new symbols using the ``update_mapping()`` function.
  
          """
          if ex.is_Atom:
              if ex is S.ImaginaryUnit:
                  if ex not in mapping:
                      return update_mapping(ex, 2, 1)
                  else:
                      return symbols[ex]
              elif ex.is_Rational:
                  return ex
          elif ex.is_Add:
              return Add(*[bottom_up_scan(g) for g in ex.args])
          elif ex.is_Mul:
              return Mul(*[bottom_up_scan(g) for g in ex.args])
          elif ex.is_Pow:
              if ex.exp.is_Rational:
                  if ex.exp < 0:
                      minpoly_base = _minpoly_groebner(ex.base, x, cls)
                      inverse = invert(x, minpoly_base).as_expr()
                      base_inv = inverse.subs(x, ex.base).expand()
  
                      if ex.exp == -1:
                          return bottom_up_scan(base_inv)
                      else:
                          ex = base_inv ** (-ex.exp)
                  if not ex.exp.is_Integer:
                      base, exp = (
                          ex.base**ex.exp.p
                      ).expand(), Rational(1, ex.exp.q)
                  else:
                      base, exp = ex.base, ex.exp
                  base = bottom_up_scan(base)
                  expr = base**exp
  
                  if expr not in mapping:
                      if exp.is_Integer:
                          return expr.expand()
                      else:
                          return update_mapping(expr, 1 / exp, -base)
                  else:
                      return symbols[expr]
          elif ex.is_AlgebraicNumber:
              if ex not in mapping:
                  return update_mapping(ex, ex.minpoly_of_element())
              else:
                  return symbols[ex]
  
          raise NotAlgebraic(
              "%s does not seem to be an algebraic number" % ex
          )
  
      def simpler_inverse(ex):
          """
          Returns True if it is more likely that the minimal polynomial
          algorithm works better with the inverse
          """
          if ex.is_Pow:
              if (1 / ex.exp).is_integer and ex.exp < 0:
                  if ex.base.is_Add:
                      return True
          if ex.is_Mul:
              hit = True
              for p in ex.args:
                  if p.is_Add:
                      return False
                  if p.is_Pow:
                      if p.base.is_Add and p.exp > 0:
                          return False
  
              if hit:
                  return True
          return False
  
      inverted = False
      ex = expand_multinomial(ex)
      if ex.is_AlgebraicNumber:
          return ex.minpoly_of_element().as_expr(x)
      elif ex.is_Rational:
          result = ex.q * x - ex.p
      else:
          inverted = simpler_inverse(ex)
          if inverted:
              ex = ex**-1
          res = None
          if ex.is_Pow and (1 / ex.exp).is_Integer:
              n = 1 / ex.exp
              res = _minimal_polynomial_sq(ex.base, n, x)
  
          elif _is_sum_surds(ex):
              res = _minimal_polynomial_sq(ex, S.One, x)
  
          if res is not None:
              result = res
  
          if res is None:
              bus = bottom_up_scan(ex)
              F = [x - bus] + list(mapping.values())
              G = groebner(F, list(symbols.values()) + [x], order="lex")
  
              _, factors = factor_list(G[-1])
              # by construction G[-1] has root `ex`
              result = _choose_factor(factors, x, ex)
      if inverted:
          result = _invertx(result, x)
          if result.coeff(x ** degree(result, x)) < 0:
              result = expand_mul(-result)
  
      return result
  
imports: from functools import reduce
  from sympy.core.add import Add
  from sympy.core.exprtools import Factors
  from sympy.core.function import expand_mul, expand_multinomial, _mexpand
  from sympy.core.mul import Mul
  from sympy.core.numbers import I, Rational, pi, _illegal
  from sympy.core.singleton import S
  from sympy.core.symbol import Dummy
  from sympy.core.sympify import sympify
  from sympy.core.traversal import preorder_traversal
  from sympy.functions.elementary.exponential import exp
  from sympy.functions.elementary.miscellaneous import sqrt, cbrt
  from sympy.functions.elementary.trigonometric import cos, sin, tan
  from sympy.ntheory.factor_ import divisors
  from sympy.utilities.iterables import subsets
  from sympy.polys.domains import ZZ, QQ, FractionField
  from sympy.polys.orthopolys import dup_chebyshevt
  from sympy.polys.polyerrors import NotAlgebraic, GeneratorsError
  from sympy.polys.polytools import Poly, PurePoly, invert, factor_list, groebner, resultant, degree, poly_from_expr, parallel_poly_from_expr, lcm
  from sympy.polys.polyutils import dict_from_expr, expr_from_dict
  from sympy.polys.ring_series import rs_compose_add
  from sympy.polys.rings import ring
  from sympy.polys.rootoftools import CRootOf
  from sympy.polys.specialpolys import cyclotomic_poly
  from sympy.utilities import numbered_symbols, public, sift
label_desc: Use image node_id 18 for calling a global function with example usage: _minpoly_groebner(ex, x, cls) and returns: result, a, False, ex, Add, True, update_mapping, symbols, ex, Mul, True, False, False, symbols, update_mapping, symbols, bottom_up_scan, expr, update_mapping