input: Write a function to decode LibSVM input into tensors using TensorFlow. The function `decode_libsvm` 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 returns 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 2:1.0 3:2.0", "2 3:3.0"], 3).label.tolist() == [1, 2]
  assert decode_libsvm(["1 2:1.0 3:2.0", "2 3:3.0"], 3, dtype=tf.float64).feature_values.dtype == tf.float64
  assert decode_libsvm([], 1).label.tolist() == []
  assert decode_libsvm(["1 2:1.0 3:2.0", "2 3:3.0"], 3, label_dtype=tf.float32).label.dtype == tf.float32
  
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)
          pairs = tf.strings.split(parts[1:], ':')
          indices = tf.strings.to_number(pairs[:, 0], out_type=tf.int64)
          values = tf.strings.to_number(pairs[:, 1], out_type=dtype)
          return label, indices, values
  
      labels, indices, values = tf.map_fn(
          parse_line,
          input,
          dtype=(label_dtype, tf.int64, dtype),
          name=name
      )
  
      feature_indices = tf.RaggedTensor.from_row_splits(
          tf.reshape(indices, [-1]),
          tf.cumsum(tf.map_fn(lambda x: tf.size(x[1]), (labels, indices, values)))
      ).flat_values
  
      feature_values = tf.RaggedTensor.from_row_splits(
          tf.reshape(values, [-1]),
          tf.cumsum(tf.map_fn(lambda x: tf.size(x[2]), (labels, indices, values)))
      ).flat_values
  
      feature_shape = tf.constant([tf.size(input), num_features], dtype=tf.int64)
  
      return labels, feature_indices, feature_values, feature_shape
  