use std::{marker::PhantomData, num::NonZeroUsize, ptr};
use cugraph_sys::{
cugraph_core_number, cugraph_core_result_get_core_numbers, cugraph_core_result_get_vertices, cugraph_core_result_t,
cugraph_error_t, cugraph_k_core, cugraph_k_core_degree_type_t, cugraph_k_core_degree_type_t_K_CORE_DEGREE_TYPE_IN,
cugraph_k_core_degree_type_t_K_CORE_DEGREE_TYPE_INOUT, cugraph_k_core_degree_type_t_K_CORE_DEGREE_TYPE_OUT,
cugraph_k_core_result_get_dst_vertices, cugraph_k_core_result_get_src_vertices, cugraph_k_core_result_get_weights,
cugraph_k_core_result_t, cugraph_type_erased_device_array_view_t,
};
use crate::{
array::RawDeviceArrayView,
cuda::{DeviceResourceKind, drop_device_resource},
cudf_interop::{core_number_result_to_dataframe, k_core_result_to_dataframe},
error::{Error, Result, check_error, require_non_null},
graph::Graph,
handle::{EnteredContext, ResourceHandleLease},
structure::typed_results::{IntegerValues, ScoreValues, VertexIds},
types::to_bool_t,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum KCoreDegreeType {
In,
Out,
#[default]
InOut,
}
impl From<KCoreDegreeType> for cugraph_k_core_degree_type_t {
fn from(value: KCoreDegreeType) -> Self {
match value {
KCoreDegreeType::In => cugraph_k_core_degree_type_t_K_CORE_DEGREE_TYPE_IN,
KCoreDegreeType::Out => cugraph_k_core_degree_type_t_K_CORE_DEGREE_TYPE_OUT,
KCoreDegreeType::InOut => cugraph_k_core_degree_type_t_K_CORE_DEGREE_TYPE_INOUT,
}
}
}
#[derive(Debug, Clone, Copy, Default)]
#[must_use]
pub struct CoreNumberParams {
pub degree_type: KCoreDegreeType,
pub do_expensive_check: bool,
}
#[derive(Debug, Clone, Copy)]
#[must_use]
pub struct KCoreParams {
pub k: NonZeroUsize,
pub degree_type: KCoreDegreeType,
pub do_expensive_check: bool,
}
impl Default for KCoreParams {
fn default() -> Self {
Self {
k: unsafe { NonZeroUsize::new_unchecked(2) },
degree_type: KCoreDegreeType::default(),
do_expensive_check: false,
}
}
}
pub(crate) fn core_number<T>(
handle: &EnteredContext<'_>,
graph: &mut Graph<T>,
params: &CoreNumberParams,
) -> Result<CoreNumberResult> {
let mut result: *mut cugraph_core_result_t = ptr::null_mut();
let mut error: *mut cugraph_error_t = ptr::null_mut();
let graph_ptr = graph.as_ptr(handle, "core_number")?;
unsafe {
let code = cugraph_core_number(
handle.handle_ptr(),
graph_ptr,
params.degree_type.into(),
to_bool_t(params.do_expensive_check),
&mut result,
&mut error,
);
check_error(code, error, handle.device(), "core_number")?;
}
let result = require_non_null(result, "core_number", "core_result")?;
unsafe { CoreNumberResult::from_raw(result, handle.device(), handle.runtime_bound_lease()) }
}
pub(crate) fn k_core<T>(
handle: &EnteredContext<'_>,
graph: &mut Graph<T>,
params: &KCoreParams,
core_numbers: Option<&CoreNumberResult>,
) -> Result<KCoreResult> {
let mut result: *mut cugraph_k_core_result_t = ptr::null_mut();
let mut error: *mut cugraph_error_t = ptr::null_mut();
let core_result_ptr = core_numbers.map(CoreNumberResult::as_ptr).unwrap_or(ptr::null());
let graph_ptr = graph.as_ptr(handle, "k_core")?;
unsafe {
let code = cugraph_k_core(
handle.handle_ptr(),
graph_ptr,
params.k.get(),
params.degree_type.into(),
core_result_ptr,
to_bool_t(params.do_expensive_check),
&mut result,
&mut error,
);
check_error(code, error, handle.device(), "k_core")?;
}
let result = require_non_null(result, "k_core", "k_core_result")?;
unsafe { KCoreResult::from_raw(result, handle.device(), handle.runtime_bound_lease()) }
}
pub struct CoreNumberResult {
raw: *mut cugraph_core_result_t,
device: i32,
_resource_handle_lease: Option<ResourceHandleLease>,
_nosend_nosync: PhantomData<*mut ()>,
}
impl std::fmt::Debug for CoreNumberResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CoreNumberResult").finish_non_exhaustive()
}
}
impl CoreNumberResult {
pub(crate) unsafe fn from_raw(
raw: *mut cugraph_core_result_t,
device: i32,
resource_handle_lease: Option<ResourceHandleLease>,
) -> Result<Self> {
let raw = require_non_null(raw, "CoreNumberResult::from_raw", "core_result")?;
Ok(Self { raw, device, _resource_handle_lease: resource_handle_lease, _nosend_nosync: PhantomData })
}
pub(crate) fn as_ptr(&self) -> *const cugraph_core_result_t {
self.raw.cast_const()
}
fn raw_view(
&self,
getter: unsafe extern "C" fn(
*mut cugraph_core_result_t,
) -> *mut cugraph_sys::cugraph_type_erased_device_array_view_t,
operation: &'static str,
pointer_name: &'static str,
) -> Result<RawDeviceArrayView<'_>> {
let view_ptr = require_non_null(
unsafe { getter(self.raw) },
operation,
pointer_name,
)?;
unsafe { RawDeviceArrayView::from_owned_view(view_ptr, Some(self.device), operation, pointer_name) }
}
pub fn vertex_ids(&self) -> Result<VertexIds<'_>> {
VertexIds::from_raw(self.raw_view(
cugraph_core_result_get_vertices,
"CoreNumberResult::vertex_ids",
"vertices",
)?)
}
pub fn core_numbers(&self) -> Result<IntegerValues<'_>> {
IntegerValues::from_raw(self.raw_view(
cugraph_core_result_get_core_numbers,
"CoreNumberResult::core_numbers",
"core_numbers",
)?)
}
pub fn to_dataframe(&self, runtime: &cudf::Runtime) -> Result<cudf::DataFrame> {
core_number_result_to_dataframe(self, runtime)
.map_err(|e: Error| e.with_operation("CoreNumberResult::to_dataframe"))
}
}
impl Drop for CoreNumberResult {
fn drop(&mut self) {
drop_device_resource(&mut self.raw, self.device, "CoreNumberResult::drop", DeviceResourceKind::CoreResult);
}
}
pub struct KCoreResult {
raw: *mut cugraph_k_core_result_t,
device: i32,
_resource_handle_lease: Option<ResourceHandleLease>,
_nosend_nosync: PhantomData<*mut ()>,
}
impl std::fmt::Debug for KCoreResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("KCoreResult").finish_non_exhaustive()
}
}
impl KCoreResult {
pub(crate) unsafe fn from_raw(
raw: *mut cugraph_k_core_result_t,
device: i32,
resource_handle_lease: Option<ResourceHandleLease>,
) -> Result<Self> {
let raw = require_non_null(raw, "KCoreResult::from_raw", "k_core_result")?;
Ok(Self { raw, device, _resource_handle_lease: resource_handle_lease, _nosend_nosync: PhantomData })
}
fn raw_view(
&self,
getter: unsafe extern "C" fn(*mut cugraph_k_core_result_t) -> *mut cugraph_type_erased_device_array_view_t,
operation: &'static str,
pointer_name: &'static str,
) -> Result<RawDeviceArrayView<'_>> {
let view_ptr = require_non_null(
unsafe { getter(self.raw) },
operation,
pointer_name,
)?;
unsafe { RawDeviceArrayView::from_owned_view(view_ptr, Some(self.device), operation, pointer_name) }
}
fn optional_raw_view(
&self,
getter: unsafe extern "C" fn(*mut cugraph_k_core_result_t) -> *mut cugraph_type_erased_device_array_view_t,
operation: &'static str,
) -> Result<Option<RawDeviceArrayView<'_>>> {
let view_ptr = unsafe { getter(self.raw) };
if view_ptr.is_null() {
return Ok(None);
}
unsafe { RawDeviceArrayView::from_owned_view(view_ptr, Some(self.device), operation, "optional_view") }
.map(Some)
}
pub fn sources(&self) -> Result<VertexIds<'_>> {
VertexIds::from_raw(self.raw_view(cugraph_k_core_result_get_src_vertices, "KCoreResult::sources", "sources")?)
}
pub fn destinations(&self) -> Result<VertexIds<'_>> {
VertexIds::from_raw(self.raw_view(
cugraph_k_core_result_get_dst_vertices,
"KCoreResult::destinations",
"destinations",
)?)
}
pub fn weights(&self) -> Result<Option<ScoreValues<'_>>> {
self.optional_raw_view(cugraph_k_core_result_get_weights, "KCoreResult::weights")?
.map(ScoreValues::from_raw)
.transpose()
}
pub fn to_dataframe(&self, runtime: &cudf::Runtime) -> Result<cudf::DataFrame> {
k_core_result_to_dataframe(self, runtime).map_err(|e: Error| e.with_operation("KCoreResult::to_dataframe"))
}
}
impl Drop for KCoreResult {
fn drop(&mut self) {
drop_device_resource(&mut self.raw, self.device, "KCoreResult::drop", DeviceResourceKind::KCoreResult);
}
}
#[cfg(test)]
mod tests {
use std::ptr;
use super::*;
use crate::error::ErrorKind;
#[test]
fn core_number_result_from_raw_rejects_null() {
let err = match unsafe { CoreNumberResult::from_raw(ptr::null_mut(), 0, None) } {
Err(err) => err,
Ok(_) => panic!("null must be rejected"),
};
assert_eq!(err.kind(), ErrorKind::Unexpected);
assert_eq!(err.operation(), "CoreNumberResult::from_raw");
}
#[test]
fn k_core_result_from_raw_rejects_null() {
let err = match unsafe { KCoreResult::from_raw(ptr::null_mut(), 0, None) } {
Err(err) => err,
Ok(_) => panic!("null must be rejected"),
};
assert_eq!(err.kind(), ErrorKind::Unexpected);
assert_eq!(err.operation(), "KCoreResult::from_raw");
}
}