use std::{marker::PhantomData, ptr};
use cugraph_sys::{
cugraph_degrees, cugraph_degrees_result_get_in_degrees, cugraph_degrees_result_get_out_degrees,
cugraph_degrees_result_get_vertices, cugraph_degrees_result_t, cugraph_error_t, cugraph_in_degrees,
cugraph_out_degrees, cugraph_type_erased_device_array_view_t,
};
use crate::{
array::{DeviceArrayView, RawDeviceArrayView},
cuda::{DeviceResourceKind, drop_device_resource},
cudf_interop::{degrees_result_to_dataframe, in_degrees_result_to_dataframe, out_degrees_result_to_dataframe},
error::{Error, ErrorKind, Result, check_error, require_non_null},
graph::{Graph, Transposed},
handle::{EnteredContext, ResourceHandleLease},
structure::typed_results::{VertexIds, widen_values},
types::{CugraphElement, DataType, to_bool_t},
};
pub(crate) fn degrees<V: CugraphElement>(
handle: &EnteredContext<'_>,
graph: &mut Graph<Transposed>,
vertices: Option<&DeviceArrayView<'_, V>>,
do_expensive_check: bool,
) -> Result<DegreesResult> {
let mut result: *mut cugraph_degrees_result_t = ptr::null_mut();
let mut error: *mut cugraph_error_t = ptr::null_mut();
let vertices_ptr = vertices.map(|v| v.as_ptr() as *const _).unwrap_or(ptr::null());
let graph_ptr = graph.as_ptr(handle, "degrees")?;
unsafe {
let code = cugraph_degrees(
handle.handle_ptr(),
graph_ptr,
vertices_ptr,
to_bool_t(do_expensive_check),
&mut result,
&mut error,
);
check_error(code, error, handle.device(), "degrees")?;
}
DegreesResult::from_raw(result, handle.device(), handle.runtime_bound_lease())
}
pub(crate) fn in_degrees<V: CugraphElement>(
handle: &EnteredContext<'_>,
graph: &mut Graph<Transposed>,
vertices: Option<&DeviceArrayView<'_, V>>,
do_expensive_check: bool,
) -> Result<InDegreesResult> {
let mut result: *mut cugraph_degrees_result_t = ptr::null_mut();
let mut error: *mut cugraph_error_t = ptr::null_mut();
let vertices_ptr = vertices.map(|v| v.as_ptr() as *const _).unwrap_or(ptr::null());
let graph_ptr = graph.as_ptr(handle, "in_degrees")?;
unsafe {
let code = cugraph_in_degrees(
handle.handle_ptr(),
graph_ptr,
vertices_ptr,
to_bool_t(do_expensive_check),
&mut result,
&mut error,
);
check_error(code, error, handle.device(), "in_degrees")?;
}
InDegreesResult::from_raw(result, handle.device(), handle.runtime_bound_lease())
}
pub(crate) fn out_degrees<T, V: CugraphElement>(
handle: &EnteredContext<'_>,
graph: &mut Graph<T>,
vertices: Option<&DeviceArrayView<'_, V>>,
do_expensive_check: bool,
) -> Result<OutDegreesResult> {
let mut result: *mut cugraph_degrees_result_t = ptr::null_mut();
let mut error: *mut cugraph_error_t = ptr::null_mut();
let vertices_ptr = vertices.map(|v| v.as_ptr() as *const _).unwrap_or(ptr::null());
let graph_ptr = graph.as_ptr(handle, "out_degrees")?;
unsafe {
let code = cugraph_out_degrees(
handle.handle_ptr(),
graph_ptr,
vertices_ptr,
to_bool_t(do_expensive_check),
&mut result,
&mut error,
);
check_error(code, error, handle.device(), "out_degrees")?;
}
OutDegreesResult::from_raw(result, handle.device(), handle.runtime_bound_lease())
}
#[derive(Debug)]
pub enum DegreeValues<'a> {
I32(DeviceArrayView<'a, i32>),
I64(DeviceArrayView<'a, i64>),
}
impl<'a> DegreeValues<'a> {
pub(crate) fn from_raw(raw: RawDeviceArrayView<'a>) -> Result<Self> {
match raw.dtype()? {
DataType::Int32 => Ok(Self::I32(DeviceArrayView::from_raw_unchecked(raw))),
DataType::Int64 => Ok(Self::I64(DeviceArrayView::from_raw_unchecked(raw))),
other => Err(Error::new(ErrorKind::InvalidState, "DegreeValues::from_raw")
.with_message(format!("unexpected degree dtype: {other}"))),
}
}
pub fn len(&self) -> usize {
match self {
Self::I32(v) => v.len(),
Self::I64(v) => v.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn to_vec_i64(&self, handle: &EnteredContext<'_>) -> Result<Vec<i64>> {
match self {
Self::I32(v) => v.to_vec(handle).map(widen_values),
Self::I64(v) => v.to_vec(handle),
}
}
pub fn to_vec_i32(&self, handle: &EnteredContext<'_>) -> Result<Vec<i32>> {
match self {
Self::I32(v) => v.to_vec(handle),
Self::I64(_) => Err(Error::new(ErrorKind::InvalidState, "DegreeValues::to_vec_i32")
.with_message("degree values are i64; use to_vec_i64() to avoid data loss")),
}
}
}
struct DegreeResultInner {
raw: *mut cugraph_degrees_result_t,
device: i32,
_resource_handle_lease: Option<ResourceHandleLease>,
_nosend_nosync: PhantomData<*mut ()>,
}
impl DegreeResultInner {
fn new(
raw: *mut cugraph_degrees_result_t,
device: i32,
operation: &'static str,
resource_handle_lease: Option<ResourceHandleLease>,
) -> Result<Self> {
let raw = require_non_null(raw, operation, "degrees_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_degrees_result_t) -> *mut cugraph_type_erased_device_array_view_t,
operation: &'static str,
pointer_name: &'static str,
) -> Result<RawDeviceArrayView<'_>> {
unsafe { RawDeviceArrayView::from_owned_view(getter(self.raw), Some(self.device), operation, pointer_name) }
}
fn vertex_ids(&self, operation: &'static str) -> Result<VertexIds<'_>> {
VertexIds::from_raw(self.raw_view(cugraph_degrees_result_get_vertices, operation, "vertices")?)
}
fn in_degrees(&self, operation: &'static str) -> Result<DegreeValues<'_>> {
DegreeValues::from_raw(self.raw_view(cugraph_degrees_result_get_in_degrees, operation, "in_degrees")?)
}
fn out_degrees(&self, operation: &'static str) -> Result<DegreeValues<'_>> {
DegreeValues::from_raw(self.raw_view(cugraph_degrees_result_get_out_degrees, operation, "out_degrees")?)
}
}
impl Drop for DegreeResultInner {
fn drop(&mut self) {
drop_device_resource(&mut self.raw, self.device, "DegreeResultInner::drop", DeviceResourceKind::DegreesResult);
}
}
pub struct DegreesResult(DegreeResultInner);
impl std::fmt::Debug for DegreesResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DegreesResult").finish_non_exhaustive()
}
}
impl DegreesResult {
fn from_raw(
raw: *mut cugraph_degrees_result_t,
device: i32,
resource_handle_lease: Option<ResourceHandleLease>,
) -> Result<Self> {
Ok(Self(DegreeResultInner::new(raw, device, "degrees", resource_handle_lease)?))
}
pub fn vertex_ids(&self) -> Result<VertexIds<'_>> {
self.0.vertex_ids("DegreesResult::vertex_ids")
}
pub fn in_degrees(&self) -> Result<DegreeValues<'_>> {
self.0.in_degrees("DegreesResult::in_degrees")
}
pub fn out_degrees(&self) -> Result<DegreeValues<'_>> {
self.0.out_degrees("DegreesResult::out_degrees")
}
pub fn to_dataframe(&self, runtime: &cudf::Runtime) -> Result<cudf::DataFrame> {
degrees_result_to_dataframe(self, runtime).map_err(|e| e.with_operation("DegreesResult::to_dataframe"))
}
}
pub struct InDegreesResult(DegreeResultInner);
impl std::fmt::Debug for InDegreesResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("InDegreesResult").finish_non_exhaustive()
}
}
impl InDegreesResult {
fn from_raw(
raw: *mut cugraph_degrees_result_t,
device: i32,
resource_handle_lease: Option<ResourceHandleLease>,
) -> Result<Self> {
Ok(Self(DegreeResultInner::new(raw, device, "in_degrees", resource_handle_lease)?))
}
pub fn vertex_ids(&self) -> Result<VertexIds<'_>> {
self.0.vertex_ids("InDegreesResult::vertex_ids")
}
pub fn in_degrees(&self) -> Result<DegreeValues<'_>> {
self.0.in_degrees("InDegreesResult::in_degrees")
}
pub fn to_dataframe(&self, runtime: &cudf::Runtime) -> Result<cudf::DataFrame> {
in_degrees_result_to_dataframe(self, runtime).map_err(|e| e.with_operation("InDegreesResult::to_dataframe"))
}
}
pub struct OutDegreesResult(DegreeResultInner);
impl std::fmt::Debug for OutDegreesResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OutDegreesResult").finish_non_exhaustive()
}
}
impl OutDegreesResult {
fn from_raw(
raw: *mut cugraph_degrees_result_t,
device: i32,
resource_handle_lease: Option<ResourceHandleLease>,
) -> Result<Self> {
Ok(Self(DegreeResultInner::new(raw, device, "out_degrees", resource_handle_lease)?))
}
pub fn vertex_ids(&self) -> Result<VertexIds<'_>> {
self.0.vertex_ids("OutDegreesResult::vertex_ids")
}
pub fn out_degrees(&self) -> Result<DegreeValues<'_>> {
self.0.out_degrees("OutDegreesResult::out_degrees")
}
pub fn to_dataframe(&self, runtime: &cudf::Runtime) -> Result<cudf::DataFrame> {
out_degrees_result_to_dataframe(self, runtime).map_err(|e| e.with_operation("OutDegreesResult::to_dataframe"))
}
}