1use std::{ffi::CString, num::NonZeroUsize, ptr};
4
5use cugraph_sys::{
6 cugraph_biased_random_walks, cugraph_compression_type_t, cugraph_compression_type_t_COO,
7 cugraph_compression_type_t_CSC, cugraph_compression_type_t_CSR, cugraph_compression_type_t_DCSC,
8 cugraph_compression_type_t_DCSR, cugraph_error_t, cugraph_homogeneous_biased_neighbor_sample,
9 cugraph_homogeneous_biased_temporal_neighbor_sample, cugraph_homogeneous_uniform_neighbor_sample,
10 cugraph_homogeneous_uniform_temporal_neighbor_sample, cugraph_prior_sources_behavior_t,
11 cugraph_prior_sources_behavior_t_CARRY_OVER, cugraph_prior_sources_behavior_t_DEFAULT,
12 cugraph_prior_sources_behavior_t_EXCLUDE, cugraph_random_walk_result_get_max_path_length,
13 cugraph_random_walk_result_get_paths, cugraph_random_walk_result_get_weights, cugraph_random_walk_result_t,
14 cugraph_sample_result_get_edge_end_time, cugraph_sample_result_get_edge_id,
15 cugraph_sample_result_get_edge_renumber_map, cugraph_sample_result_get_edge_renumber_map_offsets,
16 cugraph_sample_result_get_edge_start_time, cugraph_sample_result_get_edge_type,
17 cugraph_sample_result_get_edge_weight, cugraph_sample_result_get_hop, cugraph_sample_result_get_label_hop_offsets,
18 cugraph_sample_result_get_label_type_hop_offsets, cugraph_sample_result_get_major_offsets,
19 cugraph_sample_result_get_majors, cugraph_sample_result_get_minors, cugraph_sample_result_get_renumber_map,
20 cugraph_sample_result_get_renumber_map_offsets, cugraph_sample_result_get_start_labels, cugraph_sample_result_t,
21 cugraph_sampling_options_create, cugraph_sampling_options_free, cugraph_sampling_options_t,
22 cugraph_sampling_set_compress_per_hop, cugraph_sampling_set_compression_type, cugraph_sampling_set_dedupe_sources,
23 cugraph_sampling_set_disjoint_sampling, cugraph_sampling_set_prior_sources_behavior,
24 cugraph_sampling_set_renumber_results, cugraph_sampling_set_retain_seeds, cugraph_sampling_set_return_hops,
25 cugraph_sampling_set_temporal_sampling_comparison, cugraph_sampling_set_with_replacement,
26 cugraph_select_random_vertices, cugraph_temporal_sampling_comparison_t,
27 cugraph_temporal_sampling_comparison_t_LAST, cugraph_temporal_sampling_comparison_t_MONOTONICALLY_DECREASING,
28 cugraph_temporal_sampling_comparison_t_MONOTONICALLY_INCREASING,
29 cugraph_temporal_sampling_comparison_t_STRICTLY_DECREASING,
30 cugraph_temporal_sampling_comparison_t_STRICTLY_INCREASING, cugraph_type_erased_device_array_t,
31 cugraph_uniform_random_walks,
32};
33
34use crate::{
35 array::{DeviceArray, DeviceArrayView, HostArrayView, RawDeviceArrayView},
36 cuda::{DeviceResourceKind, drop_device_resource},
37 error::{Error, ErrorKind, Result, check_error, check_host_error, require_non_null},
38 graph::{Forward, Graph},
39 handle::{EnteredContext, ResourceHandleLease},
40 params::PositiveF64,
41 random::RngState,
42 structure::{IntegerValues, ScoreValues, SizeValues, VertexIds},
43 types::{CugraphElement, DataType, to_bool_t},
44};
45
46#[derive(Debug, Clone, Copy)]
48#[must_use]
49pub struct RandomWalkParams {
50 pub max_length: NonZeroUsize,
52}
53
54impl Default for RandomWalkParams {
55 fn default() -> Self {
56 Self { max_length: NonZeroUsize::new(1).expect("literal is non-zero") }
57 }
58}
59
60#[derive(Debug, Clone, Copy)]
62#[must_use]
63pub struct Node2VecRandomWalkParams {
64 pub max_length: NonZeroUsize,
66 pub p: PositiveF64,
68 pub q: PositiveF64,
70}
71
72impl Default for Node2VecRandomWalkParams {
73 fn default() -> Self {
74 Self {
75 max_length: NonZeroUsize::new(1).expect("literal is non-zero"),
76 p: PositiveF64::new(1.0).expect("literal is positive"),
77 q: PositiveF64::new(1.0).expect("literal is positive"),
78 }
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
84#[must_use]
85pub enum SamplingCompression {
86 #[default]
87 Coo,
88 Csr,
89 Csc,
90 Dcsr,
91 Dcsc,
92}
93
94impl SamplingCompression {
95 fn to_c(self) -> cugraph_compression_type_t {
96 match self {
97 Self::Coo => cugraph_compression_type_t_COO,
98 Self::Csr => cugraph_compression_type_t_CSR,
99 Self::Csc => cugraph_compression_type_t_CSC,
100 Self::Dcsr => cugraph_compression_type_t_DCSR,
101 Self::Dcsc => cugraph_compression_type_t_DCSC,
102 }
103 }
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
108#[must_use]
109pub enum PriorSourcesBehavior {
110 #[default]
111 Default,
112 CarryOver,
113 Exclude,
114}
115
116impl PriorSourcesBehavior {
117 fn to_c(self) -> cugraph_prior_sources_behavior_t {
118 match self {
119 Self::Default => cugraph_prior_sources_behavior_t_DEFAULT,
120 Self::CarryOver => cugraph_prior_sources_behavior_t_CARRY_OVER,
121 Self::Exclude => cugraph_prior_sources_behavior_t_EXCLUDE,
122 }
123 }
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
128#[must_use]
129pub enum TemporalSamplingComparison {
130 #[default]
131 StrictlyIncreasing,
132 MonotonicallyIncreasing,
133 StrictlyDecreasing,
134 MonotonicallyDecreasing,
135 Last,
136}
137
138impl TemporalSamplingComparison {
139 fn to_c(self) -> cugraph_temporal_sampling_comparison_t {
140 match self {
141 Self::StrictlyIncreasing => cugraph_temporal_sampling_comparison_t_STRICTLY_INCREASING,
142 Self::MonotonicallyIncreasing => cugraph_temporal_sampling_comparison_t_MONOTONICALLY_INCREASING,
143 Self::StrictlyDecreasing => cugraph_temporal_sampling_comparison_t_STRICTLY_DECREASING,
144 Self::MonotonicallyDecreasing => cugraph_temporal_sampling_comparison_t_MONOTONICALLY_DECREASING,
145 Self::Last => cugraph_temporal_sampling_comparison_t_LAST,
146 }
147 }
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
152#[must_use]
153pub struct SamplingOptions {
154 pub retain_seeds: bool,
155 pub renumber_results: bool,
156 pub compress_per_hop: bool,
157 pub with_replacement: bool,
158 pub return_hops: bool,
159 pub compression: SamplingCompression,
160 pub prior_sources_behavior: PriorSourcesBehavior,
161 pub dedupe_sources: bool,
162 pub temporal_sampling_comparison: TemporalSamplingComparison,
163 pub disjoint_sampling: bool,
164}
165
166#[derive(Debug, Clone, PartialEq, Eq)]
168#[must_use]
169pub struct NeighborSamplingParams {
170 pub fan_out: Vec<i32>,
172 pub options: SamplingOptions,
173 pub do_expensive_check: bool,
174}
175
176impl NeighborSamplingParams {
177 pub fn new(fan_out: Vec<i32>) -> Self {
178 Self { fan_out, options: SamplingOptions::default(), do_expensive_check: false }
179 }
180}
181
182impl Default for NeighborSamplingParams {
183 fn default() -> Self {
184 Self::new(vec![1])
185 }
186}
187
188struct NativeSamplingOptions {
189 raw: *mut cugraph_sampling_options_t,
190 _nosend_nosync: std::marker::PhantomData<*mut ()>,
191}
192
193impl NativeSamplingOptions {
194 fn new(options: &SamplingOptions) -> Result<Self> {
195 let mut raw: *mut cugraph_sampling_options_t = ptr::null_mut();
196 let mut error: *mut cugraph_error_t = ptr::null_mut();
197
198 unsafe {
200 let code = cugraph_sampling_options_create(&mut raw, &mut error);
201 check_host_error(code, error, "NativeSamplingOptions::new")?;
202 }
203 let raw = require_non_null(raw, "NativeSamplingOptions::new", "sampling_options")?;
204
205 unsafe {
208 cugraph_sampling_set_retain_seeds(raw, to_bool_t(options.retain_seeds));
209 cugraph_sampling_set_renumber_results(raw, to_bool_t(options.renumber_results));
210 cugraph_sampling_set_compress_per_hop(raw, to_bool_t(options.compress_per_hop));
211 cugraph_sampling_set_with_replacement(raw, to_bool_t(options.with_replacement));
212 cugraph_sampling_set_return_hops(raw, to_bool_t(options.return_hops));
213 cugraph_sampling_set_compression_type(raw, options.compression.to_c());
214 cugraph_sampling_set_prior_sources_behavior(raw, options.prior_sources_behavior.to_c());
215 cugraph_sampling_set_dedupe_sources(raw, to_bool_t(options.dedupe_sources));
216 cugraph_sampling_set_temporal_sampling_comparison(raw, options.temporal_sampling_comparison.to_c());
217 cugraph_sampling_set_disjoint_sampling(raw, to_bool_t(options.disjoint_sampling));
218 }
219
220 Ok(Self { raw, _nosend_nosync: std::marker::PhantomData })
221 }
222
223 fn as_ptr(&self) -> *const cugraph_sampling_options_t {
224 self.raw
225 }
226}
227
228impl Drop for NativeSamplingOptions {
229 fn drop(&mut self) {
230 if !self.raw.is_null() {
231 unsafe { cugraph_sampling_options_free(self.raw) };
235 }
236 }
237}
238
239pub struct RandomWalkResult {
241 raw: *mut cugraph_random_walk_result_t,
242 device: i32,
243 has_weights: bool,
244 _resource_handle_lease: Option<ResourceHandleLease>,
246 _nosend_nosync: std::marker::PhantomData<*mut ()>,
247}
248
249impl std::fmt::Debug for RandomWalkResult {
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251 f.debug_struct("RandomWalkResult")
252 .field("max_path_length", &self.max_path_length())
253 .field("has_weights", &self.has_weights)
254 .finish_non_exhaustive()
255 }
256}
257
258impl RandomWalkResult {
259 pub(crate) unsafe fn from_raw(
264 raw: *mut cugraph_random_walk_result_t,
265 device: i32,
266 has_weights: bool,
267 resource_handle_lease: Option<ResourceHandleLease>,
268 ) -> Result<Self> {
269 let raw = require_non_null(raw, "RandomWalkResult::from_raw", "random_walk_result")?;
270 Ok(Self {
271 raw,
272 device,
273 has_weights,
274 _resource_handle_lease: resource_handle_lease,
275 _nosend_nosync: std::marker::PhantomData,
276 })
277 }
278
279 pub fn max_path_length(&self) -> usize {
281 unsafe { cugraph_random_walk_result_get_max_path_length(self.raw) }
283 }
284
285 pub fn paths(&self) -> Result<VertexIds<'_>> {
291 VertexIds::from_raw(self.raw_view(cugraph_random_walk_result_get_paths, "RandomWalkResult::paths", "paths")?)
292 }
293
294 pub fn weights(&self) -> Result<Option<ScoreValues<'_>>> {
299 if !self.has_weights {
300 return Ok(None);
301 }
302
303 let raw = self.raw_view(cugraph_random_walk_result_get_weights, "RandomWalkResult::weights", "weights")?;
304 ScoreValues::from_raw(raw).map(Some)
305 }
306
307 fn raw_view(
308 &self,
309 getter: unsafe extern "C" fn(
310 *mut cugraph_random_walk_result_t,
311 ) -> *mut cugraph_sys::cugraph_type_erased_device_array_view_t,
312 operation: &'static str,
313 pointer_name: &'static str,
314 ) -> Result<RawDeviceArrayView<'_>> {
315 let view_ptr = require_non_null(
316 unsafe { getter(self.raw) },
319 operation,
320 pointer_name,
321 )?;
322 unsafe { RawDeviceArrayView::from_owned_view(view_ptr, Some(self.device), operation, pointer_name) }
325 }
326}
327
328impl Drop for RandomWalkResult {
329 fn drop(&mut self) {
330 drop_device_resource(
331 &mut self.raw,
332 self.device,
333 "RandomWalkResult::drop",
334 DeviceResourceKind::RandomWalkResult,
335 );
336 }
337}
338
339pub struct SampleResult {
341 raw: *mut cugraph_sample_result_t,
342 device: i32,
343 _resource_handle_lease: Option<ResourceHandleLease>,
345 _nosend_nosync: std::marker::PhantomData<*mut ()>,
346}
347
348impl std::fmt::Debug for SampleResult {
349 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
350 f.debug_struct("SampleResult").finish_non_exhaustive()
351 }
352}
353
354impl SampleResult {
355 pub(crate) unsafe fn from_raw(
360 raw: *mut cugraph_sample_result_t,
361 device: i32,
362 resource_handle_lease: Option<ResourceHandleLease>,
363 ) -> Result<Self> {
364 let raw = require_non_null(raw, "SampleResult::from_raw", "sample_result")?;
365 Ok(Self {
366 raw,
367 device,
368 _resource_handle_lease: resource_handle_lease,
369 _nosend_nosync: std::marker::PhantomData,
370 })
371 }
372
373 pub fn majors(&self) -> Result<Option<VertexIds<'_>>> {
375 self.optional_raw_view(cugraph_sample_result_get_majors, "SampleResult::majors")
376 .and_then(|raw| raw.map(VertexIds::from_raw).transpose())
377 }
378
379 pub fn minors(&self) -> Result<VertexIds<'_>> {
381 VertexIds::from_raw(self.required_raw_view(
382 cugraph_sample_result_get_minors,
383 "SampleResult::minors",
384 "minors",
385 )?)
386 }
387
388 pub fn major_offsets(&self) -> Result<Option<SizeValues<'_>>> {
390 self.optional_raw_view(cugraph_sample_result_get_major_offsets, "SampleResult::major_offsets")
391 .and_then(|raw| raw.map(SizeValues::from_raw).transpose())
392 }
393
394 pub fn start_labels(&self) -> Result<Option<IntegerValues<'_>>> {
396 self.optional_raw_view(cugraph_sample_result_get_start_labels, "SampleResult::start_labels")
397 .and_then(|raw| raw.map(IntegerValues::from_raw).transpose())
398 }
399
400 pub fn edge_ids(&self) -> Result<Option<IntegerValues<'_>>> {
402 self.optional_raw_view(cugraph_sample_result_get_edge_id, "SampleResult::edge_ids")
403 .and_then(|raw| raw.map(IntegerValues::from_raw).transpose())
404 }
405
406 pub fn edge_types(&self) -> Result<Option<IntegerValues<'_>>> {
408 self.optional_raw_view(cugraph_sample_result_get_edge_type, "SampleResult::edge_types")
409 .and_then(|raw| raw.map(IntegerValues::from_raw).transpose())
410 }
411
412 pub fn edge_weights(&self) -> Result<Option<ScoreValues<'_>>> {
414 self.optional_raw_view(cugraph_sample_result_get_edge_weight, "SampleResult::edge_weights")
415 .and_then(|raw| raw.map(ScoreValues::from_raw).transpose())
416 }
417
418 pub fn edge_start_times(&self) -> Result<Option<IntegerValues<'_>>> {
420 self.optional_raw_view(cugraph_sample_result_get_edge_start_time, "SampleResult::edge_start_times")
421 .and_then(|raw| raw.map(IntegerValues::from_raw).transpose())
422 }
423
424 pub fn edge_end_times(&self) -> Result<Option<IntegerValues<'_>>> {
426 self.optional_raw_view(cugraph_sample_result_get_edge_end_time, "SampleResult::edge_end_times")
427 .and_then(|raw| raw.map(IntegerValues::from_raw).transpose())
428 }
429
430 pub fn hops(&self) -> Result<Option<IntegerValues<'_>>> {
432 self.optional_raw_view(cugraph_sample_result_get_hop, "SampleResult::hops")
433 .and_then(|raw| raw.map(IntegerValues::from_raw).transpose())
434 }
435
436 pub fn label_hop_offsets(&self) -> Result<Option<SizeValues<'_>>> {
438 self.optional_raw_view(cugraph_sample_result_get_label_hop_offsets, "SampleResult::label_hop_offsets")
439 .and_then(|raw| raw.map(SizeValues::from_raw).transpose())
440 }
441
442 pub fn label_type_hop_offsets(&self) -> Result<Option<SizeValues<'_>>> {
444 self.optional_raw_view(cugraph_sample_result_get_label_type_hop_offsets, "SampleResult::label_type_hop_offsets")
445 .and_then(|raw| raw.map(SizeValues::from_raw).transpose())
446 }
447
448 pub fn renumber_map(&self) -> Result<Option<VertexIds<'_>>> {
450 self.optional_raw_view(cugraph_sample_result_get_renumber_map, "SampleResult::renumber_map")
451 .and_then(|raw| raw.map(VertexIds::from_raw).transpose())
452 }
453
454 pub fn renumber_map_offsets(&self) -> Result<Option<SizeValues<'_>>> {
456 self.optional_raw_view(cugraph_sample_result_get_renumber_map_offsets, "SampleResult::renumber_map_offsets")
457 .and_then(|raw| raw.map(SizeValues::from_raw).transpose())
458 }
459
460 pub fn edge_renumber_map(&self) -> Result<Option<IntegerValues<'_>>> {
462 self.optional_raw_view(cugraph_sample_result_get_edge_renumber_map, "SampleResult::edge_renumber_map")
463 .and_then(|raw| raw.map(IntegerValues::from_raw).transpose())
464 }
465
466 pub fn edge_renumber_map_offsets(&self) -> Result<Option<SizeValues<'_>>> {
468 self.optional_raw_view(
469 cugraph_sample_result_get_edge_renumber_map_offsets,
470 "SampleResult::edge_renumber_map_offsets",
471 )
472 .and_then(|raw| raw.map(SizeValues::from_raw).transpose())
473 }
474
475 fn optional_raw_view(
476 &self,
477 getter: unsafe extern "C" fn(
478 *const cugraph_sample_result_t,
479 ) -> *mut cugraph_sys::cugraph_type_erased_device_array_view_t,
480 operation: &'static str,
481 ) -> Result<Option<RawDeviceArrayView<'_>>> {
482 let view_ptr = unsafe { getter(self.raw) };
485 if view_ptr.is_null() {
486 return Ok(None);
487 }
488
489 unsafe { RawDeviceArrayView::from_owned_view(view_ptr, Some(self.device), operation, "view").map(Some) }
492 }
493
494 fn required_raw_view(
495 &self,
496 getter: unsafe extern "C" fn(
497 *const cugraph_sample_result_t,
498 ) -> *mut cugraph_sys::cugraph_type_erased_device_array_view_t,
499 operation: &'static str,
500 pointer_name: &'static str,
501 ) -> Result<RawDeviceArrayView<'_>> {
502 self.optional_raw_view(getter, operation)?.ok_or_else(|| {
503 Error::new(ErrorKind::InvalidState, operation)
504 .with_message(format!("native sample result returned null {pointer_name}"))
505 })
506 }
507}
508
509impl Drop for SampleResult {
510 fn drop(&mut self) {
511 drop_device_resource(&mut self.raw, self.device, "SampleResult::drop", DeviceResourceKind::SampleResult);
512 }
513}
514
515type RandomWalkFn = unsafe extern "C" fn(
516 *const cugraph_sys::cugraph_resource_handle_t,
517 *mut cugraph_sys::cugraph_rng_state_t,
518 *mut cugraph_sys::cugraph_graph_t,
519 *const cugraph_sys::cugraph_type_erased_device_array_view_t,
520 usize,
521 *mut *mut cugraph_random_walk_result_t,
522 *mut *mut cugraph_error_t,
523) -> cugraph_sys::cugraph_error_code_t;
524
525pub(crate) fn uniform_random_walks<V: CugraphElement>(
526 handle: &EnteredContext<'_>,
527 rng_state: &mut RngState,
528 graph: &mut Graph<Forward>,
529 start_vertices: &DeviceArrayView<'_, V>,
530 params: &RandomWalkParams,
531) -> Result<RandomWalkResult> {
532 run_random_walks(
533 handle,
534 rng_state,
535 graph,
536 start_vertices,
537 params.max_length.get(),
538 cugraph_uniform_random_walks,
539 "uniform_random_walks",
540 )
541}
542
543pub(crate) fn biased_random_walks<V: CugraphElement>(
544 handle: &EnteredContext<'_>,
545 rng_state: &mut RngState,
546 graph: &mut Graph<Forward>,
547 start_vertices: &DeviceArrayView<'_, V>,
548 params: &RandomWalkParams,
549) -> Result<RandomWalkResult> {
550 run_random_walks(
551 handle,
552 rng_state,
553 graph,
554 start_vertices,
555 params.max_length.get(),
556 cugraph_biased_random_walks,
557 "biased_random_walks",
558 )
559}
560
561fn run_random_walks<V: CugraphElement>(
562 handle: &EnteredContext<'_>,
563 rng_state: &mut RngState,
564 graph: &mut Graph<Forward>,
565 start_vertices: &DeviceArrayView<'_, V>,
566 max_length: usize,
567 function: RandomWalkFn,
568 operation: &'static str,
569) -> Result<RandomWalkResult> {
570 graph.validate_vertex_input_dtype(V::data_type(), operation, "start_vertices")?;
571 let mut result: *mut cugraph_random_walk_result_t = ptr::null_mut();
572 let mut error: *mut cugraph_error_t = ptr::null_mut();
573 let graph_ptr = graph.as_ptr(handle, operation)?;
574
575 unsafe {
578 let code = function(
579 handle.handle_ptr(),
580 rng_state.as_ptr(),
581 graph_ptr,
582 start_vertices.as_ptr(),
583 max_length,
584 &mut result,
585 &mut error,
586 );
587 check_error(code, error, handle.device(), operation)?;
588 }
589
590 unsafe { RandomWalkResult::from_raw(result, handle.device(), graph.has_weights(), handle.runtime_bound_lease()) }
592}
593
594pub(crate) fn node2vec_random_walks<V: CugraphElement>(
595 handle: &EnteredContext<'_>,
596 rng_state: &mut RngState,
597 graph: &mut Graph<Forward>,
598 start_vertices: &DeviceArrayView<'_, V>,
599 params: &Node2VecRandomWalkParams,
600) -> Result<RandomWalkResult> {
601 graph.validate_vertex_input_dtype(V::data_type(), "node2vec_random_walks", "start_vertices")?;
602 let mut result: *mut cugraph_random_walk_result_t = ptr::null_mut();
603 let mut error: *mut cugraph_error_t = ptr::null_mut();
604 let graph_ptr = graph.as_ptr(handle, "node2vec_random_walks")?;
605
606 unsafe {
609 let code = cugraph_sys::cugraph_node2vec_random_walks(
610 handle.handle_ptr(),
611 rng_state.as_ptr(),
612 graph_ptr,
613 start_vertices.as_ptr(),
614 params.max_length.get(),
615 params.p.get(),
616 params.q.get(),
617 &mut result,
618 &mut error,
619 );
620 check_error(code, error, handle.device(), "node2vec_random_walks")?;
621 }
622
623 unsafe { RandomWalkResult::from_raw(result, handle.device(), graph.has_weights(), handle.runtime_bound_lease()) }
625}
626
627pub(crate) fn select_random_vertices<T, V>(
628 handle: &EnteredContext<'_>,
629 graph: &Graph<T>,
630 rng_state: &mut RngState,
631 num_vertices: usize,
632) -> Result<DeviceArray<V>>
633where
634 V: CugraphElement,
635{
636 graph.validate_vertex_input_dtype(V::data_type(), "select_random_vertices", "vertices")?;
637 let mut vertices: *mut cugraph_type_erased_device_array_t = ptr::null_mut();
638 let mut error: *mut cugraph_error_t = ptr::null_mut();
639 let graph_ptr = graph.as_ptr(handle, "select_random_vertices")?;
640
641 unsafe {
644 let code = cugraph_select_random_vertices(
645 handle.handle_ptr(),
646 graph_ptr,
647 rng_state.as_ptr(),
648 num_vertices,
649 &mut vertices,
650 &mut error,
651 );
652 check_error(code, error, handle.device(), "select_random_vertices")?;
653 }
654
655 unsafe {
657 DeviceArray::from_raw(
658 vertices,
659 handle.device(),
660 "select_random_vertices",
661 "vertices",
662 handle.runtime_bound_lease(),
663 )
664 }
665}
666
667fn fan_out_view(params: &NeighborSamplingParams) -> Result<HostArrayView<'_>> {
668 if params.fan_out.is_empty() {
669 return Err(Error::new(ErrorKind::InvalidInput, "NeighborSamplingParams::fan_out")
670 .with_message("fan_out must contain at least one hop"));
671 }
672
673 let byte_len = params.fan_out.len() * std::mem::size_of::<i32>();
674 let bytes = unsafe { std::slice::from_raw_parts(params.fan_out.as_ptr().cast::<u8>(), byte_len) };
677 HostArrayView::new(bytes, params.fan_out.len(), DataType::Int32)
678}
679
680pub(crate) fn homogeneous_uniform_neighbor_sample<V: CugraphElement>(
681 handle: &EnteredContext<'_>,
682 rng_state: &mut RngState,
683 graph: &mut Graph<Forward>,
684 start_vertices: &DeviceArrayView<'_, V>,
685 starting_vertex_label_offsets: Option<&DeviceArrayView<'_, usize>>,
686 params: &NeighborSamplingParams,
687) -> Result<SampleResult> {
688 graph.validate_vertex_input_dtype(V::data_type(), "homogeneous_uniform_neighbor_sample", "start_vertices")?;
689 let fan_out = fan_out_view(params)?;
690 let options = NativeSamplingOptions::new(¶ms.options)?;
691 let mut result: *mut cugraph_sample_result_t = ptr::null_mut();
692 let mut error: *mut cugraph_error_t = ptr::null_mut();
693 let label_offsets = starting_vertex_label_offsets.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
694 let graph_ptr = graph.as_ptr(handle, "homogeneous_uniform_neighbor_sample")?;
695
696 unsafe {
699 let code = cugraph_homogeneous_uniform_neighbor_sample(
700 handle.handle_ptr(),
701 rng_state.as_ptr(),
702 graph_ptr,
703 start_vertices.as_ptr(),
704 label_offsets,
705 fan_out.as_ptr(),
706 options.as_ptr(),
707 to_bool_t(params.do_expensive_check),
708 &mut result,
709 &mut error,
710 );
711 check_error(code, error, handle.device(), "homogeneous_uniform_neighbor_sample")?;
712 }
713
714 unsafe { SampleResult::from_raw(result, handle.device(), handle.runtime_bound_lease()) }
716}
717
718pub(crate) fn homogeneous_biased_neighbor_sample<V: CugraphElement>(
719 handle: &EnteredContext<'_>,
720 rng_state: &mut RngState,
721 graph: &mut Graph<Forward>,
722 start_vertices: &DeviceArrayView<'_, V>,
723 starting_vertex_label_offsets: Option<&DeviceArrayView<'_, usize>>,
724 params: &NeighborSamplingParams,
725) -> Result<SampleResult> {
726 graph.validate_vertex_input_dtype(V::data_type(), "homogeneous_biased_neighbor_sample", "start_vertices")?;
727 let fan_out = fan_out_view(params)?;
728 let options = NativeSamplingOptions::new(¶ms.options)?;
729 let mut result: *mut cugraph_sample_result_t = ptr::null_mut();
730 let mut error: *mut cugraph_error_t = ptr::null_mut();
731 let label_offsets = starting_vertex_label_offsets.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
732 let graph_ptr = graph.as_ptr(handle, "homogeneous_biased_neighbor_sample")?;
733
734 unsafe {
740 let code = cugraph_homogeneous_biased_neighbor_sample(
741 handle.handle_ptr(),
742 rng_state.as_ptr(),
743 graph_ptr,
744 ptr::null(),
745 start_vertices.as_ptr(),
746 label_offsets,
747 fan_out.as_ptr(),
748 options.as_ptr(),
749 to_bool_t(params.do_expensive_check),
750 &mut result,
751 &mut error,
752 );
753 check_error(code, error, handle.device(), "homogeneous_biased_neighbor_sample")?;
754 }
755
756 unsafe { SampleResult::from_raw(result, handle.device(), handle.runtime_bound_lease()) }
758}
759
760pub(crate) fn homogeneous_uniform_temporal_neighbor_sample<V, Tm>(
761 handle: &EnteredContext<'_>,
762 rng_state: &mut RngState,
763 graph: &mut Graph<Forward>,
764 temporal_property_name: &str,
765 start_vertices: &DeviceArrayView<'_, V>,
766 starting_vertex_times: Option<&DeviceArrayView<'_, Tm>>,
767 starting_vertex_label_offsets: Option<&DeviceArrayView<'_, usize>>,
768 params: &NeighborSamplingParams,
769) -> Result<SampleResult>
770where
771 V: CugraphElement,
772 Tm: CugraphElement,
773{
774 graph.validate_vertex_input_dtype(
775 V::data_type(),
776 "homogeneous_uniform_temporal_neighbor_sample",
777 "start_vertices",
778 )?;
779 if let Some(times) = starting_vertex_times
780 && times.len() != start_vertices.len()
781 {
782 return Err(Error::new(ErrorKind::InvalidInput, "homogeneous_uniform_temporal_neighbor_sample")
783 .with_message("starting_vertex_times length must match start_vertices length")
784 .with_context("start_vertices_len", start_vertices.len().to_string())
785 .with_context("starting_vertex_times_len", times.len().to_string()));
786 }
787
788 let temporal_property_name = CString::new(temporal_property_name).map_err(|_| {
789 Error::new(ErrorKind::InvalidInput, "homogeneous_uniform_temporal_neighbor_sample")
790 .with_message("temporal_property_name must not contain a NUL byte")
791 })?;
792 let fan_out = fan_out_view(params)?;
793 let options = NativeSamplingOptions::new(¶ms.options)?;
794 let mut result: *mut cugraph_sample_result_t = ptr::null_mut();
795 let mut error: *mut cugraph_error_t = ptr::null_mut();
796 let times = starting_vertex_times.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
797 let label_offsets = starting_vertex_label_offsets.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
798 let graph_ptr = graph.as_ptr(handle, "homogeneous_uniform_temporal_neighbor_sample")?;
799
800 unsafe {
804 let code = cugraph_homogeneous_uniform_temporal_neighbor_sample(
805 handle.handle_ptr(),
806 rng_state.as_ptr(),
807 graph_ptr,
808 temporal_property_name.as_ptr(),
809 start_vertices.as_ptr(),
810 times,
811 label_offsets,
812 fan_out.as_ptr(),
813 options.as_ptr(),
814 to_bool_t(params.do_expensive_check),
815 &mut result,
816 &mut error,
817 );
818 check_error(code, error, handle.device(), "homogeneous_uniform_temporal_neighbor_sample")?;
819 }
820
821 unsafe { SampleResult::from_raw(result, handle.device(), handle.runtime_bound_lease()) }
823}
824
825pub(crate) fn homogeneous_biased_temporal_neighbor_sample<V, Tm>(
826 handle: &EnteredContext<'_>,
827 rng_state: &mut RngState,
828 graph: &mut Graph<Forward>,
829 temporal_property_name: &str,
830 start_vertices: &DeviceArrayView<'_, V>,
831 starting_vertex_times: Option<&DeviceArrayView<'_, Tm>>,
832 starting_vertex_label_offsets: Option<&DeviceArrayView<'_, usize>>,
833 params: &NeighborSamplingParams,
834) -> Result<SampleResult>
835where
836 V: CugraphElement,
837 Tm: CugraphElement,
838{
839 graph.validate_vertex_input_dtype(
840 V::data_type(),
841 "homogeneous_biased_temporal_neighbor_sample",
842 "start_vertices",
843 )?;
844 if let Some(times) = starting_vertex_times
845 && times.len() != start_vertices.len()
846 {
847 return Err(Error::new(ErrorKind::InvalidInput, "homogeneous_biased_temporal_neighbor_sample")
848 .with_message("starting_vertex_times length must match start_vertices length")
849 .with_context("start_vertices_len", start_vertices.len().to_string())
850 .with_context("starting_vertex_times_len", times.len().to_string()));
851 }
852
853 let temporal_property_name = CString::new(temporal_property_name).map_err(|_| {
854 Error::new(ErrorKind::InvalidInput, "homogeneous_biased_temporal_neighbor_sample")
855 .with_message("temporal_property_name must not contain a NUL byte")
856 })?;
857 let fan_out = fan_out_view(params)?;
858 let options = NativeSamplingOptions::new(¶ms.options)?;
859 let mut result: *mut cugraph_sample_result_t = ptr::null_mut();
860 let mut error: *mut cugraph_error_t = ptr::null_mut();
861 let times = starting_vertex_times.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
862 let label_offsets = starting_vertex_label_offsets.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
863 let graph_ptr = graph.as_ptr(handle, "homogeneous_biased_temporal_neighbor_sample")?;
864
865 unsafe {
870 let code = cugraph_homogeneous_biased_temporal_neighbor_sample(
871 handle.handle_ptr(),
872 rng_state.as_ptr(),
873 graph_ptr,
874 temporal_property_name.as_ptr(),
875 ptr::null(),
876 start_vertices.as_ptr(),
877 times,
878 label_offsets,
879 fan_out.as_ptr(),
880 options.as_ptr(),
881 to_bool_t(params.do_expensive_check),
882 &mut result,
883 &mut error,
884 );
885 check_error(code, error, handle.device(), "homogeneous_biased_temporal_neighbor_sample")?;
886 }
887
888 unsafe { SampleResult::from_raw(result, handle.device(), handle.runtime_bound_lease()) }
890}
891
892#[cfg(test)]
893mod tests {
894 use std::ptr;
895
896 use super::*;
897 use crate::error::ErrorKind;
898
899 #[test]
900 fn random_walk_result_from_raw_rejects_null() {
901 let err = match unsafe { RandomWalkResult::from_raw(ptr::null_mut(), 0, false, None) } {
903 Err(err) => err,
904 Ok(_) => panic!("null must be rejected"),
905 };
906 assert_eq!(err.kind(), ErrorKind::Unexpected);
907 assert_eq!(err.operation(), "RandomWalkResult::from_raw");
908 }
909
910 #[test]
911 fn sample_result_from_raw_rejects_null() {
912 let err = match unsafe { SampleResult::from_raw(ptr::null_mut(), 0, None) } {
914 Err(err) => err,
915 Ok(_) => panic!("null must be rejected"),
916 };
917 assert_eq!(err.kind(), ErrorKind::Unexpected);
918 assert_eq!(err.operation(), "SampleResult::from_raw");
919 }
920
921 #[test]
922 fn neighbor_sampling_fan_out_rejects_empty() {
923 let params = NeighborSamplingParams::new(Vec::new());
924
925 let err = fan_out_view(¶ms).expect_err("empty fan_out must be rejected");
926
927 assert_eq!(err.kind(), ErrorKind::InvalidInput);
928 assert_eq!(err.operation(), "NeighborSamplingParams::fan_out");
929 }
930}