input: Write a function to decode a LibSVM input into tensors using TensorFlow. The function 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(['0 1:2 3:4'], 5)==(tf.constant([0], dtype=tf.int64), tf.constant([[0, 0], [0, 2]], dtype=tf.int64), tf.constant([2, 4], dtype=tf.float32), tf.constant([1, 5], dtype=tf.int64))
  assert decode_libsvm(['1 2:3 4:5'], 6, dtype=tf.float64)==(tf.constant([1], dtype=tf.int64), tf.constant([[0, 1], [0, 3]], dtype=tf.int64), tf.constant([3, 5], dtype=tf.float64), tf.constant([1, 6], dtype=tf.int64))
  assert decode_libsvm(['0 1:2'], 3, label_dtype=tf.int32)==(tf.constant([0], dtype=tf.int32), tf.constant([[0, 0]], dtype=tf.int64), tf.constant([2], dtype=tf.float32), tf.constant([1, 3], dtype=tf.int64))
  assert decode_libsvm(['2 3:4'], 4, name='test')==(tf.constant([2], dtype=tf.int64), tf.constant([[0, 2]], dtype=tf.int64), tf.constant([4], dtype=tf.float32), tf.constant([1, 4], dtype=tf.int64))
  assert decode_libsvm(['1 1:2 3:4'], 5, dtype=tf.int32, label_dtype=tf.float32)==(tf.constant([1], dtype=tf.float32), tf.constant([[0, 0], [0, 2]], dtype=tf.int64), tf.constant([2, 4], dtype=tf.int32), tf.constant([1, 5], 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)
          feature_parts = parts[1:]
          indices = tf.strings.to_number(tf.strings.split(feature_parts, ':')[0], out_type=tf.int64)
          values = tf.strings.to_number(tf.strings.split(feature_parts, ':')[1], out_type=dtype)
          return label, indices, values
  
      labels, indices, values = tf.map_fn(parse_line, input, dtype=(label_dtype, tf.int64, dtype))
      feature_indices = tf.RaggedTensor.from_row_splits(tf.concat(indices, axis=0), tf.cumsum(tf.map_fn(lambda x: tf.shape(x)[0], indices, dtype=tf.int64)))
      feature_values = tf.reshape(tf.concat(values, axis=0), [-1])
      feature_shape = tf.stack([tf.shape(input)[0], num_features])
      
      return labels, feature_indices.to_tensor(), feature_values, feature_shape