code: def penalty_parser(constraints, variables="x", nvars=None):
      """parse symbolic constraints into penalty constraints.
      Returns a tuple of inequality constraints and a tuple of equality constraints.
  
      Args:
          constraints (str): a string of symbolic constraints, with one constraint
              equation per line. Constraints can be equality and/or inequality
              constraints. Standard python syntax should be followed (the ``math``
              and ``numpy`` modules are already imported).
          variables (str, default='x'): desired variable name. A list of variable
              name strings is also accepted for when desired variable names
              don't have the same base, and can include variables that are not
              found in the constraints equation string.
          nvars (int, default=None): number of variables. Includes variables not
              explicitly stated in the constraints (e.g. ``x1`` in the example below).
  
      Examples:
          >>> constraints = '''
          ...     x2 = x0/2.
          ...     x0 >= 0.'''
          >>> penalty_parser(constraints, nvars=3)
          (('-(x[0] - (0.))',), ('x[2] - (x[0]/2.)',))"""
      # from mystic.tools import src
      # ndim = len(get_variables(src(func), variables))
      if list_or_tuple_or_ndarray(variables):
          if nvars is not None:
              variables = variables[:nvars]
          constraints = replace_variables(constraints, variables)
          varname = "$"
          ndim = len(variables)
      else:
          varname = variables  # varname used below instead of variables
          myvar = get_variables(constraints, variables)
          if myvar:
              ndim = max([int(v.strip(varname)) for v in myvar]) + 1
          else:
              ndim = 0
      if nvars is not None:
          ndim = nvars
  
      # Parse the constraints string
      lines = constraints.splitlines()
      eqconstraints = []
      ineqconstraints = []
      for line in lines:
          if line.strip():
              fixed = line
              # Iterate in reverse in case ndim > 9.
              indices = list(range(ndim))
              indices.reverse()
              for i in indices:
                  fixed = fixed.replace(
                      varname + str(i), "x[" + str(i) + "]"
                  )
              constraint = fixed.strip()
  
              # Replace 'spread', 'mean', and 'variance' (uses numpy, not mystic)
              if constraint.find("spread(") != -1:
                  constraint = constraint.replace("spread(", "ptp(")
              if constraint.find("mean(") != -1:
                  constraint = constraint.replace("mean(", "average(")
              if constraint.find("variance(") != -1:
                  constraint = constraint.replace("variance(", "var(")
  
              # Sorting into equality and inequality constraints, and making all
              # inequality constraints in the form expression <= 0. and all
              # equality constraints of the form expression = 0.
              split = constraint.split(">")
              direction = ">"
              if len(split) == 1:
                  split = constraint.split("<")
                  direction = "<"
              if len(split) == 1:
                  split = constraint.split("!=")
                  direction = "!="
              if len(split) == 1:
                  split = constraint.split("=")
                  direction = "="
              if len(split) == 1:
                  print("Invalid constraint: %s" % constraint)
              # Use epsilon whenever '<' or '>' is comparator
              eps = comparator(constraint)
              eps = (
                  " + e_ "
                  if eps == ">"
                  else (" - e_ " if eps == "<" else "")
              )
              eqn = {
                  "lhs": split[0].rstrip("=").strip(),
                  "rhs": split[-1].lstrip("=").strip(),
              }
              eqn["rhs"] += eps.replace(
                  "e_", "_tol(%s,tol,rel)" % eqn["rhs"]
              )
              expression = "%(lhs)s - (%(rhs)s)" % eqn
              if direction == "=":
                  eqconstraints.append(expression)
              elif direction == "<":
                  ineqconstraints.append(expression)
              elif direction == ">":
                  ineqconstraints.append("-(" + expression + ")")
              else:  # XXX: better value than 1 for when '==' is True?
                  eqconstraints.append("(" + expression + ") == 0")
  
      return tuple(ineqconstraints), tuple(eqconstraints)
  
imports: from numpy import ndarray, asarray, any
  from mystic._symbolic import solve
  from mystic.tools import list_or_tuple_or_ndarray, flatten
label_desc: Use image node_id 21 for calling a global function with example usage: penalty_parser(constraints, variables, nvars) and returns: tuple,  tuple