code: def knot_weights(
      positions: type_alias.TensorLike,
      num_knots: type_alias.TensorLike,
      degree: int,
      cyclical: bool,
      sparse_mode: bool = False,
      name: str = "bspline_knot_weights",
  ) -> Union[tf.Tensor, Tuple[tf.Tensor, tf.Tensor]]:
      """Function that converts cardinal B-spline positions to knot weights.
  
      Note:
        In the following, A1 to An are optional batch dimensions.
  
      Args:
        positions: A tensor with shape `[A1, .. An]`. Positions must be between
          `[0, C - D)` for non-cyclical and `[0, C)` for cyclical splines, where `C`
          is the number of knots and `D` is the spline degree.
        num_knots: A strictly positive `int` describing the number of knots in the
          spline.
        degree: An `int` describing the degree of the spline, which must be smaller
          than `num_knots`.
        cyclical: A `bool` describing whether the spline is cyclical.
        sparse_mode: A `bool` describing whether to return a result only for the
          knots with nonzero weights. If set to True, the function returns the
          weights of only the `degree` + 1 knots that are non-zero, as well as the
          indices of the knots.
        name: A name for this op. Defaults to "bspline_knot_weights".
  
      Returns:
        A tensor with dense weights for each control point, with the shape
        `[A1, ... An, C]` if `sparse_mode` is False.
        Otherwise, returns a tensor of shape `[A1, ... An, D + 1]` that contains the
        non-zero weights, and a tensor with the indices of the knots, with the type
        tf.int32.
  
      Raises:
        ValueError: If degree is greater than 4 or num_knots - 1, or less than 0.
        InvalidArgumentError: If positions are not in the right range.
      """
      with tf.name_scope(name):
          positions = tf.convert_to_tensor(value=positions)
  
          if degree > 4 or degree < 0:
              raise ValueError("Degree should be between 0 and 4.")
          if degree > num_knots - 1:
              raise ValueError("Degree cannot be >= number of knots.")
          if cyclical:
              positions = asserts.assert_all_in_range(
                  positions, 0.0, float(num_knots)
              )
          else:
              positions = asserts.assert_all_in_range(
                  positions, 0.0, float(num_knots - degree)
              )
  
          all_basis_functions = {
              # Maps valid degrees to functions.
              Degree.CONSTANT: _constant,
              Degree.LINEAR: _linear,
              Degree.QUADRATIC: _quadratic,
              Degree.CUBIC: _cubic,
              Degree.QUARTIC: _quartic,
          }
          basis_functions = all_basis_functions[degree]
  
          if not cyclical and num_knots - degree == 1:
              # In this case all weights are non-zero and we can just return them.
              if not sparse_mode:
                  return basis_functions(positions)
              else:
                  shift = tf.zeros_like(positions, dtype=tf.int32)
                  return basis_functions(positions), shift
  
          # shape_batch = positions.shape.as_list()
          shape_batch = tf.shape(input=positions)
          positions = tf.reshape(positions, shape=(-1,))
  
          # Calculate the nonzero weights from the decimal parts of positions.
          shift = tf.floor(positions)
          sparse_weights = basis_functions(positions - shift)
          shift = tf.cast(shift, tf.int32)
  
          if sparse_mode:
              # Returns just the weights and the shift amounts, so that tf.gather_nd on
              # the knots can be used to sparsely activate knots if needed.
              shape_weights = tf.concat(
                  (
                      shape_batch,
                      tf.constant((degree + 1,), dtype=tf.int32),
                  ),
                  axis=0,
              )
              sparse_weights = tf.reshape(
                  sparse_weights, shape=shape_weights
              )
              shift = tf.reshape(shift, shape=shape_batch)
              return sparse_weights, shift
  
          num_positions = tf.size(input=positions)
          ind_row, ind_col = tf.meshgrid(
              tf.range(num_positions, dtype=tf.int32),
              tf.range(degree + 1, dtype=tf.int32),
              indexing="ij",
          )
  
          tiled_shifts = tf.reshape(
              tf.tile(
                  tf.expand_dims(shift, axis=-1),
                  multiples=(1, degree + 1),
              ),
              shape=(-1,),
          )
          ind_col = tf.reshape(ind_col, shape=(-1,)) + tiled_shifts
          if cyclical:
              ind_col = tf.math.mod(ind_col, num_knots)
          indices = tf.stack(
              (tf.reshape(ind_row, shape=(-1,)), ind_col), axis=-1
          )
          shape_indices = tf.concat(
              (
                  tf.reshape(num_positions, shape=(1,)),
                  tf.constant((degree + 1, 2), dtype=tf.int32),
              ),
              axis=0,
          )
          indices = tf.reshape(indices, shape=shape_indices)
          shape_scatter = tf.concat(
              (
                  tf.reshape(num_positions, shape=(1,)),
                  tf.constant((num_knots,), dtype=tf.int32),
              ),
              axis=0,
          )
          weights = tf.scatter_nd(
              indices, sparse_weights, shape_scatter
          )
          shape_weights = tf.concat(
              (shape_batch, tf.constant((num_knots,), dtype=tf.int32)),
              axis=0,
          )
          return tf.reshape(weights, shape=shape_weights)
  
imports: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  import enum
  from typing import Tuple, Union
  import tensorflow
  from tensorflow_graphics.util import asserts
  from tensorflow_graphics.util import export_api
  from tensorflow_graphics.util import shape
  from tensorflow_graphics.util import type_alias
label_desc: Use image node_id 6 for calling a global function with example usage: knot_weights(positions, num_knots, degree, cyclical, sparse_mode, name) and returns: tf, sparse_weights,  shift, basis_functions, basis_functions,  shift