code: def universal(update_func, parameters):
      """
      Creates a learner which uses a CNTK function to update the parameters.
  
      Args:
          update_func: function that takes parameters and gradients as arguments and
           returns a :class:`~cntk.ops.functions.Function` that performs the
           desired updates. The returned function updates the parameters by
           means of containing :func:`~cntk.ops.assign` operations.
           If ``update_func`` does not contain :func:`~cntk.ops.assign` operations
           the parameters will not be updated.
          parameters (list): list of network parameters to tune.
           These can be obtained by the root operator's `parameters`.
  
      Returns:
          :class:`~cntk.learners.Learner`: learner instance that can be passed to
          the :class:`~cntk.train.trainer.Trainer`
  
      Examples:
          >>> def my_adagrad(parameters, gradients):
          ...     accumulators = [C.constant(0, shape=p.shape, dtype=p.dtype, name='accum') for p in parameters]
          ...     update_funcs = []
          ...     for p, g, a in zip(parameters, gradients, accumulators):
          ...         accum_new = C.assign(a, g * g)
          ...         update_funcs.append(C.assign(p, p - 0.01 * g / C.sqrt(accum_new + 1e-6)))
          ...     return C.combine(update_funcs)
          ...
          >>> x = C.input_variable((10,))
          >>> y = C.input_variable((2,))
          >>> z = C.layers.Sequential([C.layers.Dense(100, activation=C.relu), C.layers.Dense(2)])(x)
          >>> loss = C.cross_entropy_with_softmax(z, y)
          >>> learner = C.universal(my_adagrad, z.parameters)
          >>> trainer = C.Trainer(z, loss, learner)
          >>> # now trainer can be used as any other Trainer
  
      """
  
      from .. import constant
  
      args, _ = utils.get_python_function_arguments(update_func)
      if len(args) != 2:
          raise ValueError(
              "update_func must be a function that accepts two arguments (parameters, gradients)"
          )
      gradients = []
      for p in parameters:
          if any(dim < 0 for dim in p.shape):
              raise ValueError(
                  "parameter %s has inferred dimensions. Please create the learner after all parameter shapes have been determined"
                  % str(p)
              )
          gradients.append(
              constant(0, shape=p.shape, dtype=p.dtype, name="grad")
          )
      # TODO: add additional options and learning context to the parameters of the updat_func so that the update function
      #      can make use of the context and additional options
      result = update_func(parameters, gradients)
  
      return cntk_py.universal_learner(parameters, gradients, result)
  
imports: from enum import Enum, unique
  import warnings
  import numpy
  import cntk.internal.utils
  from ..None import cntk_py, NDArrayView, asarray
  from cntk.internal import typemap
  from ..internal.swig_helper import map_if_possible
label_desc: Use image node_id 24 for calling a global function with example usage: universal(update_func, parameters) and returns: cntk_py