Skip to main content

cugraph/
types.rs

1//! Fundamental types shared across the cuGraph Rust API.
2
3use std::fmt;
4
5use cugraph_sys::{bool_t, cugraph_data_type_id_t, cugraph_graph_properties_t};
6
7use crate::error::{Error, ErrorKind};
8
9/// Element data type for type-erased arrays.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum DataType {
12    Int8,
13    Int16,
14    Int32,
15    Int64,
16    Uint8,
17    Uint16,
18    Uint32,
19    Uint64,
20    Float32,
21    Float64,
22    SizeT,
23    Bool,
24}
25
26impl fmt::Display for DataType {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        let name = match self {
29            Self::Int8 => "Int8",
30            Self::Int16 => "Int16",
31            Self::Int32 => "Int32",
32            Self::Int64 => "Int64",
33            Self::Uint8 => "Uint8",
34            Self::Uint16 => "Uint16",
35            Self::Uint32 => "Uint32",
36            Self::Uint64 => "Uint64",
37            Self::Float32 => "Float32",
38            Self::Float64 => "Float64",
39            Self::SizeT => "SizeT",
40            Self::Bool => "Bool",
41        };
42        f.write_str(name)
43    }
44}
45
46impl DataType {
47    /// Size of one element in bytes.
48    pub fn size_of(&self) -> usize {
49        match self {
50            DataType::Int8 | DataType::Uint8 | DataType::Bool => 1,
51            DataType::Int16 | DataType::Uint16 => 2,
52            DataType::Int32 | DataType::Uint32 | DataType::Float32 => 4,
53            DataType::Int64 | DataType::Uint64 | DataType::Float64 | DataType::SizeT => 8,
54        }
55    }
56
57    /// Whether this is a cuGraph-supported graph index type (Int32 or Int64).
58    ///
59    /// The cuGraph C API accepts only `i32` and `i64` for vertex ids, edge ids,
60    /// edge type ids, and temporal arrays. Other integer widths are not valid.
61    pub fn is_supported_index_type(&self) -> bool {
62        matches!(self, DataType::Int32 | DataType::Int64)
63    }
64
65    /// Whether this is a float type (Float32 or Float64).
66    ///
67    /// The cuGraph C API accepts only `f32` and `f64` for edge weights.
68    pub fn is_float(&self) -> bool {
69        matches!(self, DataType::Float32 | DataType::Float64)
70    }
71}
72
73impl From<DataType> for cugraph_data_type_id_t {
74    fn from(dt: DataType) -> Self {
75        match dt {
76            DataType::Int8 => cugraph_data_type_id_t::INT8,
77            DataType::Int16 => cugraph_data_type_id_t::INT16,
78            DataType::Int32 => cugraph_data_type_id_t::INT32,
79            DataType::Int64 => cugraph_data_type_id_t::INT64,
80            DataType::Uint8 => cugraph_data_type_id_t::UINT8,
81            DataType::Uint16 => cugraph_data_type_id_t::UINT16,
82            DataType::Uint32 => cugraph_data_type_id_t::UINT32,
83            DataType::Uint64 => cugraph_data_type_id_t::UINT64,
84            DataType::Float32 => cugraph_data_type_id_t::FLOAT32,
85            DataType::Float64 => cugraph_data_type_id_t::FLOAT64,
86            DataType::SizeT => cugraph_data_type_id_t::SIZE_T,
87            DataType::Bool => cugraph_data_type_id_t::BOOL,
88        }
89    }
90}
91
92impl TryFrom<cugraph_data_type_id_t> for DataType {
93    type Error = Error;
94
95    fn try_from(dt: cugraph_data_type_id_t) -> std::result::Result<Self, Self::Error> {
96        match dt {
97            cugraph_data_type_id_t::INT8 => Ok(DataType::Int8),
98            cugraph_data_type_id_t::INT16 => Ok(DataType::Int16),
99            cugraph_data_type_id_t::INT32 => Ok(DataType::Int32),
100            cugraph_data_type_id_t::INT64 => Ok(DataType::Int64),
101            cugraph_data_type_id_t::UINT8 => Ok(DataType::Uint8),
102            cugraph_data_type_id_t::UINT16 => Ok(DataType::Uint16),
103            cugraph_data_type_id_t::UINT32 => Ok(DataType::Uint32),
104            cugraph_data_type_id_t::UINT64 => Ok(DataType::Uint64),
105            cugraph_data_type_id_t::FLOAT32 => Ok(DataType::Float32),
106            cugraph_data_type_id_t::FLOAT64 => Ok(DataType::Float64),
107            cugraph_data_type_id_t::SIZE_T => Ok(DataType::SizeT),
108            cugraph_data_type_id_t::BOOL => Ok(DataType::Bool),
109            _ => Err(Error::new(ErrorKind::UnsupportedType, "DataType::try_from")
110                .with_message(format!("unknown cugraph_data_type_id_t value: {}", dt.0))),
111        }
112    }
113}
114
115mod sealed {
116    pub trait Sealed {}
117}
118
119/// Sealed trait binding Rust element types to native [`DataType`] representations.
120///
121/// Binds generic functions to validated cuGraph numeric types. Only standard
122/// numeric types with direct cuGraph support implement this trait.
123pub trait CugraphElement: Copy + sealed::Sealed {
124    /// The cuGraph data type that this Rust type maps to.
125    fn data_type() -> DataType;
126}
127
128macro_rules! impl_cugraph_element {
129    ($($rust_ty:ty => $variant:ident),+ $(,)?) => {
130        $(
131            impl sealed::Sealed for $rust_ty {}
132            impl CugraphElement for $rust_ty {
133                fn data_type() -> DataType {
134                    DataType::$variant
135                }
136            }
137        )+
138    };
139}
140
141impl_cugraph_element! {
142    i8  => Int8,
143    i16 => Int16,
144    i32 => Int32,
145    i64 => Int64,
146    u8  => Uint8,
147    u16 => Uint16,
148    u32 => Uint32,
149    u64 => Uint64,
150    usize => SizeT,
151    f32 => Float32,
152    f64 => Float64,
153}
154
155/// Properties that describe a graph's structure.
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
157pub struct GraphProperties {
158    pub is_symmetric: bool,
159    pub is_multigraph: bool,
160}
161
162impl GraphProperties {
163    /// A directed simple graph (`is_symmetric: false`, `is_multigraph: false`).
164    ///
165    /// This is the most common configuration and matches [`Default`].
166    pub fn directed() -> Self {
167        Self { is_symmetric: false, is_multigraph: false }
168    }
169
170    /// An undirected (symmetric) simple graph (`is_symmetric: true`, `is_multigraph: false`).
171    pub fn undirected() -> Self {
172        Self { is_symmetric: true, is_multigraph: false }
173    }
174}
175
176impl Default for GraphProperties {
177    /// Returns a directed simple graph (same as [`GraphProperties::directed()`]).
178    fn default() -> Self {
179        Self::directed()
180    }
181}
182
183impl fmt::Display for GraphProperties {
184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185        let symmetry = if self.is_symmetric { "symmetric" } else { "directed" };
186        let kind = if self.is_multigraph { "multigraph" } else { "simple graph" };
187        write!(f, "{symmetry} {kind}")
188    }
189}
190
191impl From<GraphProperties> for cugraph_graph_properties_t {
192    fn from(props: GraphProperties) -> Self {
193        cugraph_graph_properties_t {
194            is_symmetric: to_bool_t(props.is_symmetric),
195            is_multigraph: to_bool_t(props.is_multigraph),
196        }
197    }
198}
199
200/// Convert a Rust `bool` to the C `bool_t` enum.
201pub(crate) fn to_bool_t(b: bool) -> bool_t {
202    if b { bool_t::TRUE } else { bool_t::FALSE }
203}
204
205/// Convert the C `bool_t` enum to a Rust `bool`, validating that the value is
206/// either TRUE or FALSE.
207pub(crate) fn try_from_bool_t(b: bool_t) -> Option<bool> {
208    if b == bool_t::TRUE {
209        Some(true)
210    } else if b == bool_t::FALSE {
211        Some(false)
212    } else {
213        None
214    }
215}
216
217#[cfg(test)]
218mod tests {
219    use super::*;
220
221    #[test]
222    fn test_data_type_roundtrip() {
223        let variants = [
224            DataType::Int8,
225            DataType::Int16,
226            DataType::Int32,
227            DataType::Int64,
228            DataType::Uint8,
229            DataType::Uint16,
230            DataType::Uint32,
231            DataType::Uint64,
232            DataType::Float32,
233            DataType::Float64,
234            DataType::SizeT,
235            DataType::Bool,
236        ];
237        for dt in &variants {
238            let c_dt: cugraph_data_type_id_t = (*dt).into();
239            let back = DataType::try_from(c_dt).expect("known cugraph_data_type_id_t");
240            assert_eq!(*dt, back, "roundtrip failed for {:?}", dt);
241        }
242    }
243
244    #[test]
245    fn test_data_type_try_from_unknown_value_returns_descriptive_error() {
246        let err = DataType::try_from(cugraph_data_type_id_t(255)).expect_err("unknown dtype should fail");
247        assert_eq!(err.kind(), ErrorKind::UnsupportedType);
248        assert_eq!(err.operation(), "DataType::try_from");
249        assert!(err.message().contains("255"));
250    }
251
252    #[test]
253    fn test_bool_conversion() {
254        assert_eq!(to_bool_t(true), bool_t::TRUE);
255        assert_eq!(to_bool_t(false), bool_t::FALSE);
256        assert_eq!(try_from_bool_t(bool_t::TRUE), Some(true));
257        assert_eq!(try_from_bool_t(bool_t::FALSE), Some(false));
258        assert_eq!(try_from_bool_t(bool_t(2)), None);
259    }
260
261    #[test]
262    fn test_graph_properties_to_c() {
263        let props = GraphProperties::undirected();
264        let c_props: cugraph_graph_properties_t = props.into();
265        assert_eq!(c_props.is_symmetric, bool_t::TRUE);
266        assert_eq!(c_props.is_multigraph, bool_t::FALSE);
267    }
268
269    #[test]
270    fn test_cugraph_element_size_matches_data_type() {
271        // Verify that size_of::<T>() matches T::data_type().size_of() for all impls.
272        assert_eq!(std::mem::size_of::<i8>(), i8::data_type().size_of());
273        assert_eq!(std::mem::size_of::<i16>(), i16::data_type().size_of());
274        assert_eq!(std::mem::size_of::<i32>(), i32::data_type().size_of());
275        assert_eq!(std::mem::size_of::<i64>(), i64::data_type().size_of());
276        assert_eq!(std::mem::size_of::<u8>(), u8::data_type().size_of());
277        assert_eq!(std::mem::size_of::<u16>(), u16::data_type().size_of());
278        assert_eq!(std::mem::size_of::<u32>(), u32::data_type().size_of());
279        assert_eq!(std::mem::size_of::<u64>(), u64::data_type().size_of());
280        assert_eq!(std::mem::size_of::<f32>(), f32::data_type().size_of());
281        assert_eq!(std::mem::size_of::<f64>(), f64::data_type().size_of());
282    }
283}