code: def homomorphism(domain, codomain, matrix):
      r"""
      Create a homomorphism object.
  
      This function tries to build a homomorphism from ``domain`` to ``codomain``
      via the matrix ``matrix``.
  
      Examples
      ========
  
      >>> from sympy import QQ
      >>> from sympy.abc import x
      >>> from sympy.polys.agca import homomorphism
  
      >>> R = QQ.old_poly_ring(x)
      >>> T = R.free_module(2)
  
      If ``domain`` is a free module generated by `e_1, \ldots, e_n`, then
      ``matrix`` should be an n-element iterable `(b_1, \ldots, b_n)` where
      the `b_i` are elements of ``codomain``. The constructed homomorphism is the
      unique homomorphism sending `e_i` to `b_i`.
  
      >>> F = R.free_module(2)
      >>> h = homomorphism(F, T, [[1, x], [x**2, 0]])
      >>> h
      Matrix([
      [1, x**2], : QQ[x]**2 -> QQ[x]**2
      [x,    0]])
      >>> h([1, 0])
      [1, x]
      >>> h([0, 1])
      [x**2, 0]
      >>> h([1, 1])
      [x**2 + 1, x]
  
      If ``domain`` is a submodule of a free module, them ``matrix`` determines
      a homomoprhism from the containing free module to ``codomain``, and the
      homomorphism returned is obtained by restriction to ``domain``.
  
      >>> S = F.submodule([1, 0], [0, x])
      >>> homomorphism(S, T, [[1, x], [x**2, 0]])
      Matrix([
      [1, x**2], : <[1, 0], [0, x]> -> QQ[x]**2
      [x,    0]])
  
      If ``domain`` is a (sub)quotient `N/K`, then ``matrix`` determines a
      homomorphism from `N` to ``codomain``. If the kernel contains `K`, this
      homomorphism descends to ``domain`` and is returned; otherwise an exception
      is raised.
  
      >>> homomorphism(S/[(1, 0)], T, [0, [x**2, 0]])
      Matrix([
      [0, x**2], : <[1, 0] + <[1, 0]>, [0, x] + <[1, 0]>, [1, 0] + <[1, 0]>> -> QQ[x]**2
      [0,    0]])
      >>> homomorphism(S/[(0, x)], T, [0, [x**2, 0]])
      Traceback (most recent call last):
      ...
      ValueError: kernel <[1, 0], [0, 0]> must contain sm, got <[0,x]>
  
      """
  
      def freepres(module):
          """
          Return a tuple ``(F, S, Q, c)`` where ``F`` is a free module, ``S`` is a
          submodule of ``F``, and ``Q`` a submodule of ``S``, such that
          ``module = S/Q``, and ``c`` is a conversion function.
          """
          if isinstance(module, FreeModule):
              return (
                  module,
                  module,
                  module.submodule(),
                  lambda x: module.convert(x),
              )
          if isinstance(module, QuotientModule):
              return (
                  module.base,
                  module.base,
                  module.killed_module,
                  lambda x: module.convert(x).data,
              )
          if isinstance(module, SubQuotientModule):
              return (
                  module.base.container,
                  module.base,
                  module.killed_module,
                  lambda x: module.container.convert(x).data,
              )
          # an ordinary submodule
          return (
              module.container,
              module,
              module.submodule(),
              lambda x: module.container.convert(x),
          )
  
      SF, SS, SQ, _ = freepres(domain)
      TF, TS, TQ, c = freepres(codomain)
      # NOTE this is probably a bit inefficient (redundant checks)
      return (
          FreeModuleHomomorphism(SF, TF, [c(x) for x in matrix])
          .restrict_domain(SS)
          .restrict_codomain(TS)
          .quotient_codomain(TQ)
          .quotient_domain(SQ)
      )
  
imports: from sympy.polys.agca.modules import Module, FreeModule, QuotientModule, SubModule, SubQuotientModule
  from sympy.polys.polyerrors import CoercionFailed
label_desc: Use image node_id 1 for calling a global function with example usage: homomorphism(domain, codomain, matrix) and returns: FreeModuleHomomorphism, module,  module,  module,  function, module,  module,  module,  function, module,  module,  module,  function, module,  module,  module,  function