code: def hermite_normal_form(A, *, D=None, check_rank=False):
      r"""
      Compute the Hermite Normal Form of :py:class:`~.DomainMatrix` *A* over
      :ref:`ZZ`.
  
      Examples
      ========
  
      >>> from sympy import ZZ
      >>> from sympy.polys.matrices import DomainMatrix
      >>> from sympy.polys.matrices.normalforms import hermite_normal_form
      >>> m = DomainMatrix([[ZZ(12), ZZ(6), ZZ(4)],
      ...                   [ZZ(3), ZZ(9), ZZ(6)],
      ...                   [ZZ(2), ZZ(16), ZZ(14)]], (3, 3), ZZ)
      >>> print(hermite_normal_form(m).to_Matrix())
      Matrix([[10, 0, 2], [0, 15, 3], [0, 0, 2]])
  
      Parameters
      ==========
  
      A : $m \times n$ ``DomainMatrix`` over :ref:`ZZ`.
  
      D : :ref:`ZZ`, optional
          Let $W$ be the HNF of *A*. If known in advance, a positive integer *D*
          being any multiple of $\det(W)$ may be provided. In this case, if *A*
          also has rank $m$, then we may use an alternative algorithm that works
          mod *D* in order to prevent coefficient explosion.
  
      check_rank : boolean, optional (default=False)
          The basic assumption is that, if you pass a value for *D*, then
          you already believe that *A* has rank $m$, so we do not waste time
          checking it for you. If you do want this to be checked (and the
          ordinary, non-modulo *D* algorithm to be used if the check fails), then
          set *check_rank* to ``True``.
  
      Returns
      =======
  
      :py:class:`~.DomainMatrix`
          The HNF of matrix *A*.
  
      Raises
      ======
  
      DMDomainError
          If the domain of the matrix is not :ref:`ZZ`, or
          if *D* is given but is not in :ref:`ZZ`.
  
      DMShapeError
          If the mod *D* algorithm is used but the matrix has more rows than
          columns.
  
      References
      ==========
  
      .. [1] Cohen, H. *A Course in Computational Algebraic Number Theory.*
         (See Algorithms 2.4.5 and 2.4.8.)
  
      """
      if not A.domain.is_ZZ:
          raise DMDomainError("Matrix must be over domain ZZ.")
      if D is not None and (
          not check_rank or A.convert_to(QQ).rank() == A.shape[0]
      ):
          return _hermite_normal_form_modulo_D(A, D)
      else:
          return _hermite_normal_form(A)
  
file_path: sympy/sympy/polys/matrices/normalforms.py
imports: from collections import defaultdict
  from .domainmatrix import DomainMatrix
  from .exceptions import DMDomainError, DMShapeError
  from sympy.ntheory.modular import symmetric_residue
  from sympy.polys.domains import QQ, ZZ
label_desc: Use image node_id 7 for calling a global function with example usage: hermite_normal_form(A) and returns: _hermite_normal_form_modulo_D, _hermite_normal_form