code: def test_gather_op(device_id, precision):
      a_data = [
          AA([[0], [1]], dtype=PRECISION_TO_TYPE[precision]),
          AA([[3], [4]], dtype=PRECISION_TO_TYPE[precision]),
      ]
      a = C.input_variable((2, 1))
      r_data = np.arange(12).reshape(6, 2).astype("f")
      r = C.parameter(shape=r_data.data, init=r_data)
      res = C.gather(r, a).eval({a: a_data})
      expectd = np.asarray(
          [[[[0.0, 1.0]], [[2.0, 3.0]]], [[[6.0, 7.0]], [[8.0, 9.0]]]]
      )
      assert np.array_equal(res, expectd)
  
      grads = C.gather(r, a).grad({a: a_data}, [r])
      expectd_grad = np.asarray(
          [[1, 1], [1, 1], [0, 0], [1, 1], [1, 1], [0, 0]],
          dtype=np.float32,
      )
      assert np.array_equal(grads, expectd_grad)
  
      # gather with indices from learning parameter (no gradients should passed through the indices -- 0s should be passed)
      indices_params = C.parameter(shape=(1,), init=1.0)
      grads = C.gather(r, (indices_params * a)).grad(
          {a: a_data}, [r, indices_params]
      )
      assert np.array_equal(grads[r], expectd_grad)
      assert np.array_equal(
          grads[indices_params], np.asarray([0.0], dtype=np.float32)
      )
  
      b_data = [
          AA([[0, 2], [1, 3]], dtype=PRECISION_TO_TYPE[precision]),
          AA([[2, 4], [3, 5]], dtype=PRECISION_TO_TYPE[precision]),
      ]
      b = C.input_variable((2, 2))
      res2 = C.gather(r, b).eval({b: b_data})
  
      expectd2 = np.asarray(
          [
              [[[0.0, 1.0], [4.0, 5.0]], [[2.0, 3.0], [6.0, 7.0]]],
              [[[4.0, 5.0], [8.0, 9.0]], [[6.0, 7.0], [10.0, 11.0]]],
          ]
      )
      assert np.array_equal(res2, expectd2)
  
      # the following small model is to test the memory reuse issue of gather node.
      x = C.input((3, 4))
      x1 = C.to_sequence(x)
      w = C.parameter((5, 6), init=1)
      z = C.gather(w, x1)
      assert z.shape == (4, 6)
      # need the unpack node to trigger memory reuse.
      f = C.sequence.unpack(z, 0, no_mask_output=True)
      y = C.input((3, 4, 6))
      loss = C.reduce_mean(C.square(f - y), axis=-1)
      loss = C.reduce_mean(loss, axis=C.Axis.all_axes())
  
      g = C.constant(0, shape=w.shape)
      u = C.assign(w, g + 1)
      learner = C.cntk_py.universal_learner([w], [g], u)
      trainer = C.trainer.Trainer(loss, [loss], [learner])
      indices = np.asarray([[[1, 2, 1, 2]]])
      input = np.repeat(np.repeat(indices, 3, axis=1), 10, axis=0)
      lable = np.full((10, 3, 4, 6), 2)
      trainer.train_minibatch({x: input, y: lable})
      # the 2nd and 3rd rows should be udpated by gradients.
      assert np.mean(w.value[1, :]) < 1
      assert np.mean(w.value[2, :]) < 1
      # the other three rows should keep as 1
      assert np.isclose(np.mean(w.value[0, :]), 1)
      assert np.isclose(np.mean(w.value[3, :]), 1)
      assert np.isclose(np.mean(w.value[4, :]), 1)
  
imports: from __future__ import division
  import numpy
  import cntk
  import pytest
  from .ops_test_utils import unittest_helper, _test_unary_op, _test_binary_op, AA, precision, PRECISION_TO_TYPE, cntk_device
  import cntk
  from cntk import Value
  from cntk.axis import Axis
  from cntk.internal import sanitize_dtype_cntk
  from ..None import constant
label_desc: Use image node_id 14 for calling a global function with example usage: test_gather_op(device_id, precision) without return types