input: Write a function to decode LibSVM input into tensors using TensorFlow. The function should take the following parameters: `input` (a `Tensor` of type `string`), `num_features` (an `int` that is `>= 1`), `dtype` (an optional `tf.DType` from: `tf.float32, tf.float64, tf.int32, tf.int64`. Defaults to `tf.float32`), `label_dtype` (an optional `tf.DType` from: `tf.float32, tf.float64, tf.int32, tf.int64`. Defaults to `tf.int64`), and `name` (a name for the operation, optional). The function should return a tuple of `Tensor` objects `(label, feature_indices, feature_values, feature_shape)`, where `label` is a `Tensor` of type `label_dtype`, `feature_indices` is a `Tensor` of type `int64`, `feature_values` is a `Tensor` of type `dtype`, and `feature_shape` is a `Tensor` of type `int64`.
  assert decode_libsvm(["1 0:1.5 1:2", "2 0:3 1:4"], 2, dtype=tf.float32, label_dtype=tf.int64) == (tf.constant([1, 2], dtype=tf.int64), tf.constant([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=tf.int64), tf.constant([1.5, 2.0, 3.0, 4.0], dtype=tf.float32), tf.constant([2, 2], dtype=tf.int64))
  assert decode_libsvm(["3 0:5.5 1:6"], 2, dtype=tf.float32, label_dtype=tf.float64) == (tf.constant([3.0], dtype=tf.float64), tf.constant([[0, 0], [0, 1]], dtype=tf.int64), tf.constant([5.5, 6.0], dtype=tf.float32), tf.constant([1, 2], dtype=tf.int64))
  assert decode_libsvm(["4 0:7 1:8"], 2, dtype=tf.int32, label_dtype=tf.int32) == (tf.constant([4], dtype=tf.int32), tf.constant([[0, 0], [0, 1]], dtype=tf.int64), tf.constant([7, 8], dtype=tf.int32), tf.constant([1, 2], dtype=tf.int64))
  assert decode_libsvm([], 2, dtype=tf.float32, label_dtype=tf.int64) == (tf.constant([], dtype=tf.int64), tf.constant([], dtype=tf.int64), tf.constant([], dtype=tf.float32), tf.constant([0, 2], dtype=tf.int64))
output: def decode_libsvm(input, num_features, dtype=tf.float32, label_dtype=tf.int64, name=None):
      def parse_line(line):
          parts = tf.strings.split([line], ' ')
          label = tf.strings.to_number(parts[0], out_type=label_dtype)
          indices = []
          values = []
          for part in parts[1:]:
              idx, val = tf.strings.split([part], ':')
              indices.append(tf.strings.to_number(idx, out_type=tf.int64))
              values.append(tf.strings.to_number(val, out_type=dtype))
          return label, indices, values
  
      labels, indices_list, values_list = tf.map_fn(
          parse_line, input, dtype=(label_dtype, tf.int64, dtype),
          infer_shape=False, name='map_parse_line'
      )
  
      labels = tf.stack(labels)
      indices = tf.reshape(tf.ragged.stack(indices_list).flat_values, [-1])
      values = tf.reshape(tf.ragged.stack(values_list).flat_values, [-1])
      feature_indices = tf.transpose(tf.stack([tf.range(tf.size(indices)), indices]))
      feature_shape = tf.stack([tf.size(labels), num_features])
  
      return labels, feature_indices, values, feature_shape