code: def permutohedral_prepare(position_vectors):
      """
      Embedding the position vectors in a high-dimensional space,
      the lattice points are stored in hash tables.
  
      The function computes:
      - translation by the nearest reminder-0
      - ranking permutation to the canonical simplex
      - barycentric weights in the canonical simplex
  
      :param position_vectors: N x d position
      :return: barycentric weights, blur neighbours points in the hyperplane
      """
      batch_size, n_voxels, n_ch = position_vectors.shape.as_list()
      n_ch_1 = n_ch + 1
  
      # reshaping batches and voxels into one dimension
      # means we can use 1D gather and hashing easily
      position_vectors = tf.reshape(position_vectors, [-1, n_ch])
  
      # Generate position vectors in lattice space
      # first rotate position into the (n_ch+1)-dimensional hyperplane
      inv_std_dev = np.sqrt(2 / 3.0) * n_ch_1
      scale_factor = tf.constant(
          [
              inv_std_dev / np.sqrt((i + 1) * (i + 2))
              for i in range(n_ch)
          ]
      )
      Ex = [None] * n_ch_1
      cum_sum = 0.0
      for dit in range(n_ch, 0, -1):
          scaled_vectors = (
              position_vectors[:, dit - 1] * scale_factor[dit - 1]
          )
          Ex[dit] = cum_sum - scaled_vectors * dit
          cum_sum += scaled_vectors
      Ex[0] = cum_sum
      Ex = tf.stack(Ex, -1)
  
      # Compute coordinates
      # Get closest remainder-0 point
      v = tf.to_int32(tf.round(Ex / float(n_ch_1)))
      rem0 = v * n_ch_1
  
      # Find the simplex we are in and store it in rank
      # (where rank describes what position coordinate i has
      # in the sorted order of the features values).
      # This can be done more efficiently
      # if necessary following the permutohedral paper.
      index = tf.nn.top_k(
          Ex - tf.to_float(rem0), n_ch_1, sorted=True
      ).indices
      rank = tf.nn.top_k(-index, n_ch_1, sorted=True).indices
  
      # if the point doesn't lie on the plane (sum != 0) bring it back
      # (sum(v) != 0)  meaning off the plane
      rank = rank + tf.reduce_sum(v, 1, True)
      add_minus_sub = tf.to_int32(rank < 0) - tf.to_int32(rank > n_ch)
      add_minus_sub *= n_ch_1
      rem0 = rem0 + add_minus_sub
      rank = rank + add_minus_sub
  
      # Compute the barycentric coordinates (p.10 in [Adams et al 2010])
      v2 = (Ex - tf.to_float(rem0)) / float(n_ch_1)
      # CRF2RNN uses the calculated ranks to get v2 sorted in O(n_ch) time
      # We cheat here by using the easy to implement
      # but slower method of sorting again in O(n_ch log n_ch)
      # we might get this even more efficient
      # if we correct the original sorted data above
      v_sorted = -tf.nn.top_k(-v2, n_ch_1, sorted=True).values
      # weighted against the canonical simplex vertices
      barycentric = v_sorted - tf.concat(
          [v_sorted[:, -1:] - 1.0, v_sorted[:, :-1]], 1
      )
  
      # Compute all vertices and their offset
      def _simple_hash(key):
          # WARNING: This hash function does not guarantee
          # uniqueness of different position_vectors
          hash_vector = np.power(
              int(np.floor(np.power(tf.int64.max, 1.0 / (n_ch + 2)))),
              [range(1, n_ch_1)],
          )
          hash_vector = tf.constant(hash_vector, dtype=tf.int64)
          return tf.reduce_sum(tf.to_int64(key) * hash_vector, 1)
  
      # This is done so if the user had TF 1.12.1 or a new version the code
      # does not brake. First part of the try is for TF 1.12.1 where the
      # deleted_key keyword was missing, while the second is just a normal
      # usage for TF 1.13.1>=
      try:
          hash_table = tf.contrib.lookup.MutableDenseHashTable(
              tf.int64,
              tf.int64,
              default_value=tf.constant([-1] * 100, dtype=tf.int64),
              empty_key=-2,
              initial_num_buckets=8,
              checkpoint=False,
          )
      except TypeError:
          hash_table = tf.contrib.lookup.MutableDenseHashTable(
              tf.int64,
              tf.int64,
              default_value=tf.constant([-1] * n_ch, dtype=tf.int64),
              empty_key=-3,
              deleted_key=-2,
              initial_num_buckets=8,
              checkpoint=False,
          )
      try:
          index_table = tf.contrib.lookup.MutableDenseHashTable(
              tf.int64,
              tf.int64,
              default_value=0,
              empty_key=-1,
              initial_num_buckets=8,
              checkpoint=False,
          )
      except TypeError:
          index_table = tf.contrib.lookup.MutableDenseHashTable(
              tf.int64,
              tf.int64,
              default_value=0,
              empty_key=-2,
              deleted_key=-1,
              initial_num_buckets=8,
              checkpoint=False,
          )
  
      # canonical simplex (p.4 in [Adams et al 2010])
      canonical = [
          [i] * (n_ch_1 - i) + [i - n_ch - 1] * i for i in range(n_ch_1)
      ]
  
      insert_ops = []
      loc = [None] * n_ch_1
      loc_hash = [None] * n_ch_1
      for scit in range(n_ch_1):
          # Compute the location of the lattice point explicitly
          # (all but the last coordinate -
          #  it's redundant because they sum to zero)
          loc[scit] = (
              tf.gather(canonical[scit], rank[:, :-1]) + rem0[:, :-1]
          )
          loc_hash[scit] = _simple_hash(loc[scit])
          insert_ops.append(
              hash_table.insert(loc_hash[scit], tf.to_int64(loc[scit]))
          )
  
      with tf.control_dependencies(insert_ops):
          fused_loc_hash, fused_loc = hash_table.export()
          is_good_key = tf.where(tf.not_equal(fused_loc_hash, -2))[:, 0]
          fused_loc = tf.gather(fused_loc, is_good_key)
          fused_loc_hash = tf.gather(fused_loc_hash, is_good_key)
  
      # The additional index hash table is used to
      # linearise the hash table so that we can `tf.scatter` and `tf.gather`
      # (range_id 0 reserved for the indextable's default value)
      range_id = tf.range(
          1,
          tf.size(fused_loc_hash, out_type=tf.int64) + 1,
          dtype=tf.int64,
      )
      range_id = tf.expand_dims(range_id, 1)
      insert_indices = index_table.insert(fused_loc_hash, range_id)
  
      # linearised [batch, spatial_dim] indices
      # where in the splat variable each simplex vertex is
      batch_index = tf.range(batch_size, dtype=tf.int32)
      batch_index = tf.expand_dims(batch_index, 1)
      batch_index = tf.tile(batch_index, [1, n_voxels])
      batch_index = tf.to_int64(tf.reshape(batch_index, [-1]))
  
      indices = [None] * n_ch_1
      blur_neighbours1 = [None] * n_ch_1
      blur_neighbours2 = [None] * n_ch_1
      with tf.control_dependencies([insert_indices]):
          for dit in range(n_ch_1):
              # the neighbors along each axis.
              offset = [n_ch if i == dit else -1 for i in range(n_ch)]
              offset = tf.constant(offset, dtype=tf.int64)
              blur_neighbours1[dit] = index_table.lookup(
                  _simple_hash(fused_loc + offset)
              )
              blur_neighbours2[dit] = index_table.lookup(
                  _simple_hash(fused_loc - offset)
              )
              indices[dit] = tf.stack(
                  [index_table.lookup(loc_hash[dit]), batch_index], 1
              )
  
      return barycentric, blur_neighbours1, blur_neighbours2, indices
  
file_path: NiftyNet/niftynet/layer/crf.py
imports: from __future__ import absolute_import, print_function
  import numpy
  import tensorflow
  from niftynet.layer.base_layer import TrainableLayer
  from niftynet.layer.layer_util import infer_spatial_rank, expand_spatial_params
label_desc: Use image node_id 2 for calling a global function with example usage: permutohedral_prepare(position_vectors) and returns: barycentric,  blur_neighbours1,  blur_neighbours2,  indices, tf