input: Write a function to decode LibSVM input to tensors using TensorFlow. The function `decode_libsvm` should take the following arguments: `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(tf.constant(["1 2:3 4:5"]), 5)[0].numpy() == 1
  assert decode_libsvm(tf.constant(["1 2:3 4:5"]), 5)[1].numpy().tolist() == [[0, 2], [0, 4]]
  assert decode_libsvm(tf.constant(["1 2:3 4:5"]), 5)[2].numpy().tolist() == [3, 5]
  assert decode_libsvm(tf.constant(["1 2:3 4:5"]), 5)[3].numpy().tolist() == [1, 5]
  assert decode_libsvm(tf.constant(["2 1:4 3:6"]), 4, dtype=tf.int32)[0].numpy() == 2
  assert decode_libsvm(tf.constant(["2 1:4 3:6"]), 4, dtype=tf.int32)[1].numpy().tolist() == [[0, 1], [0, 3]]
  assert decode_libsvm(tf.constant(["2 1:4 3:6"]), 4, dtype=tf.int32)[2].numpy().tolist() == [4, 6]
  assert decode_libsvm(tf.constant(["2 1:4 3:6"]), 4, dtype=tf.int32)[3].numpy().tolist() == [1, 4]
  assert decode_libsvm(tf.constant([]), 3)[0].numpy().tolist() == []
  assert decode_libsvm(tf.constant([]), 3)[1].numpy().tolist() == []
  assert decode_libsvm(tf.constant([]), 3)[2].numpy().tolist() == []
  assert decode_libsvm(tf.constant([]), 3)[3].numpy().tolist() == [0, 3]
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, values = tf.map_fn(
          parse_line,
          input,
          fn_output_signature=(label_dtype, tf.int64, dtype)
      )
  
      labels = tf.reshape(labels, [-1])
      indices = tf.reshape(indices, [-1, 2])
      values = tf.reshape(values, [-1])
      feature_shape = tf.shape(labels, out_type=tf.int64)[0]
  
      return labels, indices, values, tf.stack([feature_shape, tf.constant(num_features, dtype=tf.int64)])