code: def analyze_pattern(
      pattern: str, opname: str
  ) -> Tuple[int, int, int]:
      # Maybe some validation of identifiers?
      axes = pattern.split()
      axes_set = set(axes)
      if len(axes) != len(axes_set):
          raise EinopsError(
              f'Duplicates in axes names in {opname}(..., "{pattern}")'
          )
      if "*" not in axes_set:
          raise EinopsError(f'No *-axis in {opname}(..., "{pattern}")')
      for axis in axes:
          if axis != "*":
              (
                  is_valid,
                  reason,
              ) = ParsedExpression.check_axis_name_return_reason(axis)
              if not is_valid:
                  raise EinopsError(
                      f'Invalid axis name {axis} in {opname}(..., "{pattern}")'
                  )
      n_axes_before = axes.index("*")
      n_axes_after = len(axes) - n_axes_before - 1
      min_axes = n_axes_before + n_axes_after
      return n_axes_before, n_axes_after, min_axes
  
file_path: einops/einops/packing.py
imports: from functools import lru_cache
  from typing import List, Union, TypeVar, Tuple, Sequence
  from einops import EinopsError
  from einops._backends import get_backend
  from einops.parsing import ParsedExpression
label_desc: Use image node_id 1 for calling a global function with example usage: analyze_pattern(pattern, opname) and returns: n_axes_before,  n_axes_after,  min_axes