Skip to main content

cugraph/labeling/
result.rs

1//! Result wrapper for vertex labeling algorithms.
2
3use std::marker::PhantomData;
4
5use cugraph_sys::{
6    cugraph_labeling_result_get_labels, cugraph_labeling_result_get_vertices, cugraph_labeling_result_t,
7    cugraph_type_erased_device_array_view_t,
8};
9
10use crate::{
11    array::RawDeviceArrayView,
12    cuda::{DeviceResourceKind, drop_device_resource},
13    cudf_interop::labeling_result_to_dataframe,
14    error::{Error, Result, require_non_null},
15    handle::ResourceHandleLease,
16    structure::typed_results::{IntegerValues, VertexIds},
17};
18
19/// Result of weakly or strongly connected components.
20///
21/// Weakly and strongly connected components return `cugraph_labeling_result_t`
22/// with identical field layout, represented by this result type.
23pub struct LabelingResult {
24    raw: *mut cugraph_labeling_result_t,
25    device: i32,
26    // Keeps a runtime-bound cuGraph handle alive until after Drop frees `raw`.
27    _resource_handle_lease: Option<ResourceHandleLease>,
28    _nosend_nosync: PhantomData<*mut ()>,
29}
30
31impl std::fmt::Debug for LabelingResult {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        f.debug_struct("LabelingResult").finish_non_exhaustive()
34    }
35}
36
37impl LabelingResult {
38    /// Wrap a raw labeling result pointer.
39    ///
40    /// # Safety
41    ///
42    /// `raw` must be a valid, non-null pointer returned by
43    /// `cugraph_weakly_connected_components` or
44    /// `cugraph_strongly_connected_components`. Ownership is transferred to
45    /// this struct.
46    pub(crate) unsafe fn from_raw(
47        raw: *mut cugraph_labeling_result_t,
48        device: i32,
49        resource_handle_lease: Option<ResourceHandleLease>,
50    ) -> Result<Self> {
51        let raw = require_non_null(raw, "LabelingResult::from_raw", "labeling_result")?;
52        Ok(Self { raw, device, _resource_handle_lease: resource_handle_lease, _nosend_nosync: PhantomData })
53    }
54
55    fn raw_view(
56        &self,
57        getter: unsafe extern "C" fn(*mut cugraph_labeling_result_t) -> *mut cugraph_type_erased_device_array_view_t,
58        operation: &'static str,
59        pointer_name: &'static str,
60    ) -> Result<RawDeviceArrayView<'_>> {
61        let view_ptr = require_non_null(
62            // SAFETY: `self.raw` is a live labeling result owned by this wrapper.
63            unsafe { getter(self.raw) },
64            operation,
65            pointer_name,
66        )?;
67        // SAFETY: cuGraph allocates this view metadata; this wrapper frees it, and the result retains its backing arrays.
68        unsafe { RawDeviceArrayView::from_owned_view(view_ptr, Some(self.device), operation, pointer_name) }
69    }
70
71    /// Get vertex ids as a runtime-typed device array view.
72    pub fn vertex_ids(&self) -> Result<VertexIds<'_>> {
73        VertexIds::from_raw(self.raw_view(
74            cugraph_labeling_result_get_vertices,
75            "LabelingResult::vertex_ids",
76            "vertices",
77        )?)
78    }
79
80    /// Get component labels as a runtime-typed integer device array view.
81    pub fn labels(&self) -> Result<IntegerValues<'_>> {
82        IntegerValues::from_raw(self.raw_view(
83            cugraph_labeling_result_get_labels,
84            "LabelingResult::labels",
85            "labels",
86        )?)
87    }
88
89    /// Convert this result to a cudf DataFrame with `"vertex"` and `"label"` columns.
90    ///
91    /// Each `"label"` is mapped to the minimum vertex id of its component to
92    /// guarantee deterministic output across runs.
93    pub fn to_dataframe(&self, runtime: &cudf::Runtime) -> Result<cudf::DataFrame> {
94        labeling_result_to_dataframe(self, runtime).map_err(|e: Error| e.with_operation("LabelingResult::to_dataframe"))
95    }
96}
97
98impl Drop for LabelingResult {
99    fn drop(&mut self) {
100        drop_device_resource(&mut self.raw, self.device, "LabelingResult::drop", DeviceResourceKind::LabelingResult);
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use std::ptr;
107
108    use super::*;
109    use crate::error::ErrorKind;
110
111    #[test]
112    fn labeling_result_from_raw_rejects_null() {
113        // SAFETY: this exercises the constructor's null guard only.
114        let err = match unsafe { LabelingResult::from_raw(ptr::null_mut(), 0, None) } {
115            Err(err) => err,
116            Ok(_) => panic!("null must be rejected"),
117        };
118        assert_eq!(err.kind(), ErrorKind::Unexpected);
119        assert_eq!(err.operation(), "LabelingResult::from_raw");
120    }
121}