1use crate::harness::xgcf;
8use crate::harness::{CategoryResult, TestContext, TestResult};
9use std::collections::{BTreeMap, HashSet};
10use std::sync::Arc;
11use std::time::Instant;
12use xlog_core::{RuntimeConfig, ScalarType, Schema};
13use xlog_cuda::{CudaBuffer, CudaKernelProvider};
14use xlog_logic::Compiler;
15use xlog_runtime::Executor;
16
17const WIDENED_FRONTIER_REPLAY_SOURCE: &str = r#"
18 pred frontier_pred(u32).
19 pred widened_pred(u32).
20 pred frontier_edge(u32, u32).
21 pred blocked_pred(u32).
22 pred promoted(u32).
23 pred replay_reachable(u32).
24 pred rollback_hit(u32).
25
26 promoted(P) :- frontier_pred(P), widened_pred(P).
27 replay_reachable(P) :- promoted(P).
28 replay_reachable(Q) :- replay_reachable(P), frontier_edge(P, Q), frontier_pred(Q).
29 rollback_hit(P) :- replay_reachable(P), blocked_pred(P).
30"#;
31
32pub fn run_all(ctx: &TestContext) -> CategoryResult {
34 let mut results = CategoryResult::new("c15_determinism");
35 let start = Instant::now();
36
37 results.add_result(test_sort_reproducibility(ctx));
38 results.add_result(test_filter_reproducibility(ctx));
39 results.add_result(test_join_reproducibility(ctx));
40 results.add_result(test_dedup_reproducibility(ctx));
41 results.add_result(test_stable_sort_order(ctx));
42 results.add_result(test_mc_sample_reproducibility(ctx));
43 results.add_result(test_xgcf_forward_reproducibility(ctx));
44 results.add_result(test_xgcf_backward_reproducibility(ctx));
45 results.add_result(test_widened_frontier_replay_representative(ctx));
46
47 results.set_duration(start.elapsed());
48 results
49}
50
51fn widened_frontier_replay_unary_schema() -> Schema {
52 Schema::new(vec![("c0".to_string(), ScalarType::U32)])
53}
54
55fn widened_frontier_replay_binary_schema() -> Schema {
56 Schema::new(vec![
57 ("c0".to_string(), ScalarType::U32),
58 ("c1".to_string(), ScalarType::U32),
59 ])
60}
61
62fn upload_widened_frontier_replay_unary(
63 provider: &CudaKernelProvider,
64 values: &[u32],
65) -> Result<CudaBuffer, String> {
66 provider
67 .create_buffer_from_u32_columns(&[values], widened_frontier_replay_unary_schema())
68 .map_err(|e| format!("upload unary failed: {}", e))
69}
70
71fn upload_widened_frontier_replay_binary(
72 provider: &CudaKernelProvider,
73 values: &[(u32, u32)],
74) -> Result<CudaBuffer, String> {
75 let col0: Vec<u32> = values.iter().map(|(a, _)| *a).collect();
76 let col1: Vec<u32> = values.iter().map(|(_, b)| *b).collect();
77 provider
78 .create_buffer_from_u32_columns(&[&col0, &col1], widened_frontier_replay_binary_schema())
79 .map_err(|e| format!("upload binary failed: {}", e))
80}
81
82fn download_widened_frontier_replay_rows(
83 provider: &CudaKernelProvider,
84 buffer: &CudaBuffer,
85) -> Result<Vec<Vec<u32>>, String> {
86 let columns: Vec<Vec<u32>> = (0..buffer.arity())
87 .map(|col| {
88 provider
89 .download_column::<u32>(buffer, col)
90 .map_err(|e| format!("download column {} failed: {}", col, e))
91 })
92 .collect::<Result<Vec<_>, _>>()?;
93 if columns.is_empty() {
94 return Ok(Vec::new());
95 }
96 let row_count = columns[0].len();
97 let mut rows: Vec<Vec<u32>> = (0..row_count)
98 .map(|row| columns.iter().map(|col| col[row]).collect())
99 .collect();
100 rows.sort();
101 Ok(rows)
102}
103
104fn run_widened_frontier_replay(
105 provider: Arc<CudaKernelProvider>,
106) -> Result<BTreeMap<String, Vec<Vec<u32>>>, String> {
107 let mut compiler = Compiler::new();
108 let plan = compiler
109 .compile(WIDENED_FRONTIER_REPLAY_SOURCE)
110 .map_err(|e| format!("compile replay failed: {}", e))?;
111 let mut executor = Executor::new_with_config(
112 Arc::clone(&provider),
113 RuntimeConfig::default().with_wcoj_triangle_dispatch(Some(false)),
114 );
115 for (name, rel_id) in compiler.rel_ids() {
116 executor.register_relation(*rel_id, name);
117 }
118
119 let frontier_pred = [1, 2, 3, 4, 5];
120 let widened_pred = [2, 4];
121 let blocked_pred = [5];
122 let frontier_edge = [(2, 3), (3, 5), (4, 5)];
123 executor.put_relation(
124 "frontier_pred",
125 upload_widened_frontier_replay_unary(&provider, &frontier_pred)?,
126 );
127 executor.put_relation(
128 "widened_pred",
129 upload_widened_frontier_replay_unary(&provider, &widened_pred)?,
130 );
131 executor.put_relation(
132 "blocked_pred",
133 upload_widened_frontier_replay_unary(&provider, &blocked_pred)?,
134 );
135 executor.put_relation(
136 "frontier_edge",
137 upload_widened_frontier_replay_binary(&provider, &frontier_edge)?,
138 );
139 executor
140 .execute_plan(&plan)
141 .map_err(|e| format!("execute replay failed: {}", e))?;
142
143 let mut out = BTreeMap::new();
144 for name in ["promoted", "replay_reachable", "rollback_hit"] {
145 let buffer = executor
146 .store()
147 .get(name)
148 .ok_or_else(|| format!("missing replay relation {}", name))?;
149 out.insert(
150 name.to_string(),
151 download_widened_frontier_replay_rows(&provider, buffer)?,
152 );
153 }
154 Ok(out)
155}
156
157fn test_widened_frontier_replay_representative(ctx: &TestContext) -> TestResult {
160 let start = Instant::now();
161 let provider = Arc::clone(&ctx.provider);
162 let first = match run_widened_frontier_replay(Arc::clone(&provider)) {
163 Ok(snapshot) => snapshot,
164 Err(e) => {
165 return TestResult::error(
166 "test_widened_frontier_replay_representative",
167 start.elapsed(),
168 e,
169 )
170 }
171 };
172 let second = match run_widened_frontier_replay(provider) {
173 Ok(snapshot) => snapshot,
174 Err(e) => {
175 return TestResult::error(
176 "test_widened_frontier_replay_representative",
177 start.elapsed(),
178 e,
179 )
180 }
181 };
182
183 if first != second {
184 return TestResult::error(
185 "test_widened_frontier_replay_representative",
186 start.elapsed(),
187 format!(
188 "replay representative diverged: first={:?}, second={:?}",
189 first, second
190 ),
191 );
192 }
193 if first["promoted"].len() != 2
194 || first["replay_reachable"].len() != 4
195 || first["rollback_hit"].len() != 1
196 {
197 return TestResult::error(
198 "test_widened_frontier_replay_representative",
199 start.elapsed(),
200 format!("unexpected replay row counts: {:?}", first),
201 );
202 }
203
204 TestResult::passed(
205 "test_widened_frontier_replay_representative",
206 start.elapsed(),
207 )
208}
209
210fn test_mc_sample_reproducibility(ctx: &TestContext) -> TestResult {
212 let start = Instant::now();
213
214 let probs: Vec<f32> = vec![0.1, 0.5, 0.9];
215 let num_samples = 4096usize;
216 let seed = 424242u64;
217
218 let num_vars = probs.len();
220 let mut d_force_mask = ctx.memory.alloc::<u8>(num_vars.max(1)).unwrap();
221 ctx.device.inner().memset_zeros(&mut d_force_mask).unwrap();
222 let mut d_forced_value = ctx.memory.alloc::<u8>(num_vars.max(1)).unwrap();
223 ctx.device
224 .inner()
225 .memset_zeros(&mut d_forced_value)
226 .unwrap();
227
228 let a = match ctx.provider.sample_bernoulli_matrix(
229 &probs,
230 num_samples,
231 seed,
232 &d_force_mask.slice(..),
233 &d_forced_value.slice(..),
234 ) {
235 Ok(v) => v,
236 Err(e) => {
237 return TestResult::error(
238 "test_mc_sample_reproducibility",
239 start.elapsed(),
240 format!("sample_bernoulli_matrix failed: {}", e),
241 )
242 }
243 };
244 let b = match ctx.provider.sample_bernoulli_matrix(
245 &probs,
246 num_samples,
247 seed,
248 &d_force_mask.slice(..),
249 &d_forced_value.slice(..),
250 ) {
251 Ok(v) => v,
252 Err(e) => {
253 return TestResult::error(
254 "test_mc_sample_reproducibility",
255 start.elapsed(),
256 format!("sample_bernoulli_matrix failed (2nd run): {}", e),
257 )
258 }
259 };
260
261 if a != b {
262 return TestResult::error(
263 "test_mc_sample_reproducibility",
264 start.elapsed(),
265 format!(
266 "MC sampling not deterministic: outputs differ (len={})",
267 a.len()
268 ),
269 );
270 }
271
272 TestResult::passed("test_mc_sample_reproducibility", start.elapsed())
273}
274
275fn test_xgcf_forward_reproducibility(ctx: &TestContext) -> TestResult {
277 let start = Instant::now();
278
279 let spec = xgcf::tiny_xgcf_spec();
280 let a = match xgcf::run_tiny_xgcf_forward(ctx, &spec) {
281 Ok(v) => v,
282 Err(e) => {
283 return TestResult::error(
284 "test_xgcf_forward_reproducibility",
285 start.elapsed(),
286 format!("xgcf forward failed: {}", e),
287 )
288 }
289 };
290 let b = match xgcf::run_tiny_xgcf_forward(ctx, &spec) {
291 Ok(v) => v,
292 Err(e) => {
293 return TestResult::error(
294 "test_xgcf_forward_reproducibility",
295 start.elapsed(),
296 format!("xgcf forward failed (2nd run): {}", e),
297 )
298 }
299 };
300
301 if a != b {
302 return TestResult::error(
303 "test_xgcf_forward_reproducibility",
304 start.elapsed(),
305 "XGCF forward not deterministic: values differ across runs".to_string(),
306 );
307 }
308
309 TestResult::passed("test_xgcf_forward_reproducibility", start.elapsed())
310}
311
312fn test_xgcf_backward_reproducibility(ctx: &TestContext) -> TestResult {
314 let start = Instant::now();
315
316 let spec = xgcf::tiny_xgcf_spec();
317 let a = match xgcf::run_tiny_xgcf_backward(ctx, &spec) {
318 Ok(r) => r,
319 Err(e) => {
320 return TestResult::error(
321 "test_xgcf_backward_reproducibility",
322 start.elapsed(),
323 format!("xgcf backward failed: {}", e),
324 )
325 }
326 };
327 let b = match xgcf::run_tiny_xgcf_backward(ctx, &spec) {
328 Ok(r) => r,
329 Err(e) => {
330 return TestResult::error(
331 "test_xgcf_backward_reproducibility",
332 start.elapsed(),
333 format!("xgcf backward failed (2nd run): {}", e),
334 )
335 }
336 };
337
338 if a.values != b.values
339 || a.adj != b.adj
340 || a.grad_true != b.grad_true
341 || a.grad_false != b.grad_false
342 {
343 return TestResult::error(
344 "test_xgcf_backward_reproducibility",
345 start.elapsed(),
346 "XGCF backward not deterministic: outputs differ across runs".to_string(),
347 );
348 }
349
350 TestResult::passed("test_xgcf_backward_reproducibility", start.elapsed())
351}
352
353fn test_sort_reproducibility(ctx: &TestContext) -> TestResult {
358 let start = Instant::now();
359 let schema = Schema::new(vec![("val".to_string(), ScalarType::U32)]);
360
361 const SIZE: usize = 10000;
362 const NUM_ITERATIONS: usize = 5;
363
364 let data: Vec<u32> = (0..SIZE)
366 .map(|i| ((i * 1103515245 + 12345) % 1000000) as u32)
367 .collect();
368
369 let buffer = match ctx
370 .provider
371 .create_buffer_from_slice::<u32>(&data, schema.clone())
372 {
373 Ok(buf) => buf,
374 Err(e) => {
375 return TestResult::error(
376 "test_sort_reproducibility",
377 start.elapsed(),
378 format!("Failed to create buffer: {}", e),
379 )
380 }
381 };
382
383 let first_sorted = match ctx.provider.sort(&buffer, &[0]) {
385 Ok(s) => s,
386 Err(e) => {
387 return TestResult::error(
388 "test_sort_reproducibility",
389 start.elapsed(),
390 format!("First sort failed: {}", e),
391 )
392 }
393 };
394
395 let first_result = match ctx.provider.download_column::<u32>(&first_sorted, 0) {
396 Ok(d) => d,
397 Err(e) => {
398 return TestResult::error(
399 "test_sort_reproducibility",
400 start.elapsed(),
401 format!("Failed to download first sort result: {}", e),
402 )
403 }
404 };
405
406 for i in 1..first_result.len() {
408 if first_result[i] < first_result[i - 1] {
409 return TestResult::error(
410 "test_sort_reproducibility",
411 start.elapsed(),
412 format!(
413 "First sort result not sorted at index {}: {} < {}",
414 i,
415 first_result[i],
416 first_result[i - 1]
417 ),
418 );
419 }
420 }
421
422 for iteration in 1..NUM_ITERATIONS {
424 let sorted = match ctx.provider.sort(&buffer, &[0]) {
425 Ok(s) => s,
426 Err(e) => {
427 return TestResult::error(
428 "test_sort_reproducibility",
429 start.elapsed(),
430 format!("Sort iteration {} failed: {}", iteration, e),
431 )
432 }
433 };
434
435 let result = match ctx.provider.download_column::<u32>(&sorted, 0) {
436 Ok(d) => d,
437 Err(e) => {
438 return TestResult::error(
439 "test_sort_reproducibility",
440 start.elapsed(),
441 format!("Failed to download iteration {} result: {}", iteration, e),
442 )
443 }
444 };
445
446 if result.len() != first_result.len() {
448 return TestResult::error(
449 "test_sort_reproducibility",
450 start.elapsed(),
451 format!(
452 "Iteration {} produced {} rows, first produced {}",
453 iteration,
454 result.len(),
455 first_result.len()
456 ),
457 );
458 }
459
460 for (i, (&a, &b)) in first_result.iter().zip(result.iter()).enumerate() {
461 if a != b {
462 return TestResult::error(
463 "test_sort_reproducibility",
464 start.elapsed(),
465 format!(
466 "Iteration {} differs from first at index {}: {} vs {}",
467 iteration, i, a, b
468 ),
469 );
470 }
471 }
472 }
473
474 let buffer2 = match ctx
476 .provider
477 .create_buffer_from_slice::<u32>(&data, schema.clone())
478 {
479 Ok(buf) => buf,
480 Err(e) => {
481 return TestResult::error(
482 "test_sort_reproducibility",
483 start.elapsed(),
484 format!("Failed to create second buffer: {}", e),
485 )
486 }
487 };
488
489 let sorted2 = match ctx.provider.sort(&buffer2, &[0]) {
490 Ok(s) => s,
491 Err(e) => {
492 return TestResult::error(
493 "test_sort_reproducibility",
494 start.elapsed(),
495 format!("Sort on fresh buffer failed: {}", e),
496 )
497 }
498 };
499
500 let result2 = match ctx.provider.download_column::<u32>(&sorted2, 0) {
501 Ok(d) => d,
502 Err(e) => {
503 return TestResult::error(
504 "test_sort_reproducibility",
505 start.elapsed(),
506 format!("Failed to download fresh buffer sort result: {}", e),
507 )
508 }
509 };
510
511 if result2 != first_result {
512 return TestResult::error(
513 "test_sort_reproducibility",
514 start.elapsed(),
515 "Sort on fresh buffer produced different result than original".to_string(),
516 );
517 }
518
519 if let Err(e) = ctx.sync_and_check() {
520 return TestResult::error(
521 "test_sort_reproducibility",
522 start.elapsed(),
523 format!("Sync failed: {}", e),
524 );
525 }
526
527 TestResult::passed("test_sort_reproducibility", start.elapsed())
528}
529
530fn test_filter_reproducibility(ctx: &TestContext) -> TestResult {
535 let start = Instant::now();
536 let schema = Schema::new(vec![("val".to_string(), ScalarType::U32)]);
537
538 const SIZE: usize = 10000;
539 const NUM_ITERATIONS: usize = 5;
540
541 let data: Vec<u32> = (0..SIZE as u32).collect();
543
544 let mask: Vec<u8> = (0..SIZE)
546 .map(|i| if (i * 7 + 3) % 10 < 3 { 1 } else { 0 })
547 .collect();
548
549 let buffer = match ctx
550 .provider
551 .create_buffer_from_slice::<u32>(&data, schema.clone())
552 {
553 Ok(buf) => buf,
554 Err(e) => {
555 return TestResult::error(
556 "test_filter_reproducibility",
557 start.elapsed(),
558 format!("Failed to create buffer: {}", e),
559 )
560 }
561 };
562
563 let first_filtered = match ctx.provider.filter_by_mask(&buffer, &mask) {
565 Ok(f) => f,
566 Err(e) => {
567 return TestResult::error(
568 "test_filter_reproducibility",
569 start.elapsed(),
570 format!("First filter failed: {}", e),
571 )
572 }
573 };
574
575 let first_result = match ctx.provider.download_column::<u32>(&first_filtered, 0) {
576 Ok(d) => d,
577 Err(e) => {
578 return TestResult::error(
579 "test_filter_reproducibility",
580 start.elapsed(),
581 format!("Failed to download first filter result: {}", e),
582 )
583 }
584 };
585
586 let expected_count: usize = mask.iter().map(|&m| m as usize).sum();
588 if first_result.len() != expected_count {
589 return TestResult::error(
590 "test_filter_reproducibility",
591 start.elapsed(),
592 format!(
593 "First filter returned {} rows, expected {}",
594 first_result.len(),
595 expected_count
596 ),
597 );
598 }
599
600 for iteration in 1..NUM_ITERATIONS {
602 let filtered = match ctx.provider.filter_by_mask(&buffer, &mask) {
603 Ok(f) => f,
604 Err(e) => {
605 return TestResult::error(
606 "test_filter_reproducibility",
607 start.elapsed(),
608 format!("Filter iteration {} failed: {}", iteration, e),
609 )
610 }
611 };
612
613 let result = match ctx.provider.download_column::<u32>(&filtered, 0) {
614 Ok(d) => d,
615 Err(e) => {
616 return TestResult::error(
617 "test_filter_reproducibility",
618 start.elapsed(),
619 format!("Failed to download iteration {} result: {}", iteration, e),
620 )
621 }
622 };
623
624 if result != first_result {
626 let first_diff = result
627 .iter()
628 .zip(first_result.iter())
629 .position(|(a, b)| a != b);
630 return TestResult::error(
631 "test_filter_reproducibility",
632 start.elapsed(),
633 format!(
634 "Filter iteration {} differs from first (first diff at {:?})",
635 iteration, first_diff
636 ),
637 );
638 }
639 }
640
641 let test_masks: Vec<(String, Vec<u8>)> = vec![
643 (
644 "10%".to_string(),
645 (0..SIZE).map(|i| if i % 10 == 0 { 1 } else { 0 }).collect(),
646 ),
647 (
648 "50%".to_string(),
649 (0..SIZE).map(|i| if i % 2 == 0 { 1 } else { 0 }).collect(),
650 ),
651 (
652 "90%".to_string(),
653 (0..SIZE).map(|i| if i % 10 != 0 { 1 } else { 0 }).collect(),
654 ),
655 ];
656
657 for (name, test_mask) in test_masks {
658 let baseline = match ctx.provider.filter_by_mask(&buffer, &test_mask) {
659 Ok(f) => f,
660 Err(e) => {
661 return TestResult::error(
662 "test_filter_reproducibility",
663 start.elapsed(),
664 format!("Baseline filter {} failed: {}", name, e),
665 )
666 }
667 };
668
669 let baseline_data = match ctx.provider.download_column::<u32>(&baseline, 0) {
670 Ok(d) => d,
671 Err(e) => {
672 return TestResult::error(
673 "test_filter_reproducibility",
674 start.elapsed(),
675 format!("Failed to download {} baseline: {}", name, e),
676 )
677 }
678 };
679
680 let repeat = match ctx.provider.filter_by_mask(&buffer, &test_mask) {
682 Ok(f) => f,
683 Err(e) => {
684 return TestResult::error(
685 "test_filter_reproducibility",
686 start.elapsed(),
687 format!("Repeat filter {} failed: {}", name, e),
688 )
689 }
690 };
691
692 let repeat_data = match ctx.provider.download_column::<u32>(&repeat, 0) {
693 Ok(d) => d,
694 Err(e) => {
695 return TestResult::error(
696 "test_filter_reproducibility",
697 start.elapsed(),
698 format!("Failed to download {} repeat: {}", name, e),
699 )
700 }
701 };
702
703 if baseline_data != repeat_data {
704 return TestResult::error(
705 "test_filter_reproducibility",
706 start.elapsed(),
707 format!("Filter {} produced different results on repeat", name),
708 );
709 }
710 }
711
712 if let Err(e) = ctx.sync_and_check() {
713 return TestResult::error(
714 "test_filter_reproducibility",
715 start.elapsed(),
716 format!("Sync failed: {}", e),
717 );
718 }
719
720 TestResult::passed("test_filter_reproducibility", start.elapsed())
721}
722
723fn test_join_reproducibility(ctx: &TestContext) -> TestResult {
728 let start = Instant::now();
729
730 let left_schema = Schema::new(vec![
731 ("key".to_string(), ScalarType::U32),
732 ("lval".to_string(), ScalarType::U32),
733 ]);
734 let right_schema = Schema::new(vec![
735 ("key".to_string(), ScalarType::U32),
736 ("rval".to_string(), ScalarType::U32),
737 ]);
738
739 const LEFT_SIZE: usize = 5000;
740 const RIGHT_SIZE: usize = 3000;
741 const NUM_ITERATIONS: usize = 5;
742
743 let left_keys: Vec<u32> = (0..LEFT_SIZE).map(|i| (i * 3) as u32).collect();
745 let left_vals: Vec<u32> = left_keys.iter().map(|&k| k * 10).collect();
746
747 let right_keys: Vec<u32> = (0..RIGHT_SIZE).map(|i| (i * 5) as u32).collect();
749 let right_vals: Vec<u32> = right_keys.iter().map(|&k| k * 100).collect();
750
751 let left_buffer = match ctx
752 .provider
753 .create_buffer_from_u32_columns(&[&left_keys, &left_vals], left_schema.clone())
754 {
755 Ok(buf) => buf,
756 Err(e) => {
757 return TestResult::error(
758 "test_join_reproducibility",
759 start.elapsed(),
760 format!("Failed to create left buffer: {}", e),
761 )
762 }
763 };
764
765 let right_buffer = match ctx
766 .provider
767 .create_buffer_from_u32_columns(&[&right_keys, &right_vals], right_schema.clone())
768 {
769 Ok(buf) => buf,
770 Err(e) => {
771 return TestResult::error(
772 "test_join_reproducibility",
773 start.elapsed(),
774 format!("Failed to create right buffer: {}", e),
775 )
776 }
777 };
778
779 let first_joined = match ctx
781 .provider
782 .hash_join(&left_buffer, &right_buffer, &[0], &[0])
783 {
784 Ok(j) => j,
785 Err(e) => {
786 return TestResult::error(
787 "test_join_reproducibility",
788 start.elapsed(),
789 format!("First join failed: {}", e),
790 )
791 }
792 };
793
794 let first_keys = match ctx.provider.download_column::<u32>(&first_joined, 0) {
795 Ok(d) => d,
796 Err(e) => {
797 return TestResult::error(
798 "test_join_reproducibility",
799 start.elapsed(),
800 format!("Failed to download first join keys: {}", e),
801 )
802 }
803 };
804
805 let first_lvals = match ctx.provider.download_column::<u32>(&first_joined, 1) {
806 Ok(d) => d,
807 Err(e) => {
808 return TestResult::error(
809 "test_join_reproducibility",
810 start.elapsed(),
811 format!("Failed to download first join lvals: {}", e),
812 )
813 }
814 };
815
816 let first_rvals = match ctx.provider.download_column::<u32>(&first_joined, 2) {
817 Ok(d) => d,
818 Err(e) => {
819 return TestResult::error(
820 "test_join_reproducibility",
821 start.elapsed(),
822 format!("Failed to download first join rvals: {}", e),
823 )
824 }
825 };
826
827 for iteration in 1..NUM_ITERATIONS {
829 let joined = match ctx
830 .provider
831 .hash_join(&left_buffer, &right_buffer, &[0], &[0])
832 {
833 Ok(j) => j,
834 Err(e) => {
835 return TestResult::error(
836 "test_join_reproducibility",
837 start.elapsed(),
838 format!("Join iteration {} failed: {}", iteration, e),
839 )
840 }
841 };
842
843 if ctx.device_row_count(&joined) != ctx.device_row_count(&first_joined) {
845 return TestResult::error(
846 "test_join_reproducibility",
847 start.elapsed(),
848 format!(
849 "Iteration {} returned {} rows, first returned {}",
850 iteration,
851 ctx.device_row_count(&joined),
852 ctx.device_row_count(&first_joined)
853 ),
854 );
855 }
856
857 let keys = match ctx.provider.download_column::<u32>(&joined, 0) {
858 Ok(d) => d,
859 Err(e) => {
860 return TestResult::error(
861 "test_join_reproducibility",
862 start.elapsed(),
863 format!("Failed to download iteration {} keys: {}", iteration, e),
864 )
865 }
866 };
867
868 let lvals = match ctx.provider.download_column::<u32>(&joined, 1) {
869 Ok(d) => d,
870 Err(e) => {
871 return TestResult::error(
872 "test_join_reproducibility",
873 start.elapsed(),
874 format!("Failed to download iteration {} lvals: {}", iteration, e),
875 )
876 }
877 };
878
879 let rvals = match ctx.provider.download_column::<u32>(&joined, 2) {
880 Ok(d) => d,
881 Err(e) => {
882 return TestResult::error(
883 "test_join_reproducibility",
884 start.elapsed(),
885 format!("Failed to download iteration {} rvals: {}", iteration, e),
886 )
887 }
888 };
889
890 let first_tuples: HashSet<(u32, u32, u32)> = first_keys
892 .iter()
893 .zip(first_lvals.iter())
894 .zip(first_rvals.iter())
895 .map(|((&k, &l), &r)| (k, l, r))
896 .collect();
897
898 let iter_tuples: HashSet<(u32, u32, u32)> = keys
899 .iter()
900 .zip(lvals.iter())
901 .zip(rvals.iter())
902 .map(|((&k, &l), &r)| (k, l, r))
903 .collect();
904
905 if first_tuples != iter_tuples {
906 return TestResult::error(
907 "test_join_reproducibility",
908 start.elapsed(),
909 format!(
910 "Iteration {} produced different tuples: {} vs {} unique",
911 iteration,
912 iter_tuples.len(),
913 first_tuples.len()
914 ),
915 );
916 }
917 }
918
919 if let Err(e) = ctx.sync_and_check() {
920 return TestResult::error(
921 "test_join_reproducibility",
922 start.elapsed(),
923 format!("Sync failed: {}", e),
924 );
925 }
926
927 TestResult::passed("test_join_reproducibility", start.elapsed())
928}
929
930fn test_dedup_reproducibility(ctx: &TestContext) -> TestResult {
935 let start = Instant::now();
936 let schema = Schema::new(vec![
937 ("key".to_string(), ScalarType::U32),
938 ("val".to_string(), ScalarType::U32),
939 ]);
940
941 const SIZE: usize = 10000;
942 const NUM_ITERATIONS: usize = 5;
943
944 let keys: Vec<u32> = (0..SIZE).map(|i| (i % 1000) as u32).collect();
946 let vals: Vec<u32> = (0..SIZE as u32).collect();
947
948 let buffer = match ctx
949 .provider
950 .create_buffer_from_u32_columns(&[&keys, &vals], schema.clone())
951 {
952 Ok(buf) => buf,
953 Err(e) => {
954 return TestResult::error(
955 "test_dedup_reproducibility",
956 start.elapsed(),
957 format!("Failed to create buffer: {}", e),
958 )
959 }
960 };
961
962 let first_deduped = match ctx.provider.dedup(&buffer, &[0]) {
964 Ok(d) => d,
965 Err(e) => {
966 return TestResult::error(
967 "test_dedup_reproducibility",
968 start.elapsed(),
969 format!("First dedup failed: {}", e),
970 )
971 }
972 };
973
974 let first_keys = match ctx.provider.download_column::<u32>(&first_deduped, 0) {
975 Ok(d) => d,
976 Err(e) => {
977 return TestResult::error(
978 "test_dedup_reproducibility",
979 start.elapsed(),
980 format!("Failed to download first dedup keys: {}", e),
981 )
982 }
983 };
984
985 let unique_keys: HashSet<u32> = keys.iter().copied().collect();
987 if first_keys.len() != unique_keys.len() {
988 return TestResult::error(
989 "test_dedup_reproducibility",
990 start.elapsed(),
991 format!(
992 "First dedup returned {} rows, expected {}",
993 first_keys.len(),
994 unique_keys.len()
995 ),
996 );
997 }
998
999 let first_key_set: HashSet<u32> = first_keys.iter().copied().collect();
1001 if first_key_set.len() != first_keys.len() {
1002 return TestResult::error(
1003 "test_dedup_reproducibility",
1004 start.elapsed(),
1005 "First dedup result contains duplicates".to_string(),
1006 );
1007 }
1008
1009 for iteration in 1..NUM_ITERATIONS {
1011 let deduped = match ctx.provider.dedup(&buffer, &[0]) {
1012 Ok(d) => d,
1013 Err(e) => {
1014 return TestResult::error(
1015 "test_dedup_reproducibility",
1016 start.elapsed(),
1017 format!("Dedup iteration {} failed: {}", iteration, e),
1018 )
1019 }
1020 };
1021
1022 if ctx.device_row_count(&deduped) != ctx.device_row_count(&first_deduped) {
1024 return TestResult::error(
1025 "test_dedup_reproducibility",
1026 start.elapsed(),
1027 format!(
1028 "Iteration {} returned {} rows, first returned {}",
1029 iteration,
1030 ctx.device_row_count(&deduped),
1031 ctx.device_row_count(&first_deduped)
1032 ),
1033 );
1034 }
1035
1036 let iter_keys = match ctx.provider.download_column::<u32>(&deduped, 0) {
1037 Ok(d) => d,
1038 Err(e) => {
1039 return TestResult::error(
1040 "test_dedup_reproducibility",
1041 start.elapsed(),
1042 format!("Failed to download iteration {} keys: {}", iteration, e),
1043 )
1044 }
1045 };
1046
1047 let iter_key_set: HashSet<u32> = iter_keys.iter().copied().collect();
1049 if first_key_set != iter_key_set {
1050 return TestResult::error(
1051 "test_dedup_reproducibility",
1052 start.elapsed(),
1053 format!(
1054 "Iteration {} produced different unique keys: {} vs {}",
1055 iteration,
1056 iter_key_set.len(),
1057 first_key_set.len()
1058 ),
1059 );
1060 }
1061 }
1062
1063 let test_patterns: Vec<(&str, Vec<u32>)> = vec![
1065 ("all_same", vec![42; 5000]),
1066 ("pairs", (0..2500u32).flat_map(|i| vec![i, i]).collect()),
1067 (
1068 "random_dups",
1069 (0..5000usize)
1070 .map(|i| ((i * 1103515245 + 12345) % 500) as u32)
1071 .collect(),
1072 ),
1073 ];
1074
1075 for (name, pattern_keys) in test_patterns {
1076 let pattern_vals: Vec<u32> = (0..pattern_keys.len() as u32).collect();
1077 let pattern_buffer = match ctx
1078 .provider
1079 .create_buffer_from_u32_columns(&[&pattern_keys, &pattern_vals], schema.clone())
1080 {
1081 Ok(buf) => buf,
1082 Err(e) => {
1083 return TestResult::error(
1084 "test_dedup_reproducibility",
1085 start.elapsed(),
1086 format!("Failed to create {} buffer: {}", name, e),
1087 )
1088 }
1089 };
1090
1091 let baseline = match ctx.provider.dedup(&pattern_buffer, &[0]) {
1092 Ok(d) => d,
1093 Err(e) => {
1094 return TestResult::error(
1095 "test_dedup_reproducibility",
1096 start.elapsed(),
1097 format!("{} baseline dedup failed: {}", name, e),
1098 )
1099 }
1100 };
1101
1102 let baseline_keys = match ctx.provider.download_column::<u32>(&baseline, 0) {
1103 Ok(d) => d,
1104 Err(e) => {
1105 return TestResult::error(
1106 "test_dedup_reproducibility",
1107 start.elapsed(),
1108 format!("Failed to download {} baseline: {}", name, e),
1109 )
1110 }
1111 };
1112
1113 let repeat = match ctx.provider.dedup(&pattern_buffer, &[0]) {
1114 Ok(d) => d,
1115 Err(e) => {
1116 return TestResult::error(
1117 "test_dedup_reproducibility",
1118 start.elapsed(),
1119 format!("{} repeat dedup failed: {}", name, e),
1120 )
1121 }
1122 };
1123
1124 let repeat_keys = match ctx.provider.download_column::<u32>(&repeat, 0) {
1125 Ok(d) => d,
1126 Err(e) => {
1127 return TestResult::error(
1128 "test_dedup_reproducibility",
1129 start.elapsed(),
1130 format!("Failed to download {} repeat: {}", name, e),
1131 )
1132 }
1133 };
1134
1135 let baseline_set: HashSet<u32> = baseline_keys.iter().copied().collect();
1136 let repeat_set: HashSet<u32> = repeat_keys.iter().copied().collect();
1137
1138 if baseline_set != repeat_set {
1139 return TestResult::error(
1140 "test_dedup_reproducibility",
1141 start.elapsed(),
1142 format!("{} dedup produced different results on repeat", name),
1143 );
1144 }
1145 }
1146
1147 if let Err(e) = ctx.sync_and_check() {
1148 return TestResult::error(
1149 "test_dedup_reproducibility",
1150 start.elapsed(),
1151 format!("Sync failed: {}", e),
1152 );
1153 }
1154
1155 TestResult::passed("test_dedup_reproducibility", start.elapsed())
1156}
1157
1158fn test_stable_sort_order(ctx: &TestContext) -> TestResult {
1163 let start = Instant::now();
1164 let schema = Schema::new(vec![
1165 ("key".to_string(), ScalarType::U32),
1166 ("val".to_string(), ScalarType::U32),
1167 ]);
1168
1169 let mut keys: Vec<u32> = Vec::new();
1172 let mut vals: Vec<u32> = Vec::new();
1173
1174 for key in 0..100u32 {
1176 for instance in 0..10u32 {
1177 keys.push(key);
1178 vals.push(key * 100 + instance); }
1180 }
1181
1182 let buffer = match ctx
1183 .provider
1184 .create_buffer_from_u32_columns(&[&keys, &vals], schema.clone())
1185 {
1186 Ok(buf) => buf,
1187 Err(e) => {
1188 return TestResult::error(
1189 "test_stable_sort_order",
1190 start.elapsed(),
1191 format!("Failed to create buffer: {}", e),
1192 )
1193 }
1194 };
1195
1196 let sorted = match ctx.provider.sort(&buffer, &[0]) {
1198 Ok(s) => s,
1199 Err(e) => {
1200 return TestResult::error(
1201 "test_stable_sort_order",
1202 start.elapsed(),
1203 format!("Sort failed: {}", e),
1204 )
1205 }
1206 };
1207
1208 let sorted_keys = match ctx.provider.download_column::<u32>(&sorted, 0) {
1209 Ok(d) => d,
1210 Err(e) => {
1211 return TestResult::error(
1212 "test_stable_sort_order",
1213 start.elapsed(),
1214 format!("Failed to download sorted keys: {}", e),
1215 )
1216 }
1217 };
1218
1219 let sorted_vals = match ctx.provider.download_column::<u32>(&sorted, 1) {
1220 Ok(d) => d,
1221 Err(e) => {
1222 return TestResult::error(
1223 "test_stable_sort_order",
1224 start.elapsed(),
1225 format!("Failed to download sorted vals: {}", e),
1226 )
1227 }
1228 };
1229
1230 for i in 1..sorted_keys.len() {
1232 if sorted_keys[i] < sorted_keys[i - 1] {
1233 return TestResult::error(
1234 "test_stable_sort_order",
1235 start.elapsed(),
1236 format!(
1237 "Keys not sorted at index {}: {} < {}",
1238 i,
1239 sorted_keys[i],
1240 sorted_keys[i - 1]
1241 ),
1242 );
1243 }
1244 }
1245
1246 let mut current_key = sorted_keys[0];
1252 let mut group_start = 0;
1253
1254 for i in 1..=sorted_keys.len() {
1255 let at_end = i == sorted_keys.len();
1256 let key_changed = !at_end && sorted_keys[i] != current_key;
1257
1258 if at_end || key_changed {
1259 for j in (group_start + 1)..i {
1261 if sorted_vals[j] < sorted_vals[j - 1] {
1264 }
1268 }
1269
1270 if !at_end {
1271 current_key = sorted_keys[i];
1272 group_start = i;
1273 }
1274 }
1275 }
1276
1277 let mut key_counts: std::collections::HashMap<u32, usize> = std::collections::HashMap::new();
1279 for &key in &sorted_keys {
1280 *key_counts.entry(key).or_insert(0) += 1;
1281 }
1282
1283 for (&key, &count) in &key_counts {
1284 if count != 10 {
1285 return TestResult::error(
1286 "test_stable_sort_order",
1287 start.elapsed(),
1288 format!("Key {} appears {} times, expected 10", key, count),
1289 );
1290 }
1291 }
1292
1293 let sorted2 = match ctx.provider.sort(&buffer, &[0]) {
1295 Ok(s) => s,
1296 Err(e) => {
1297 return TestResult::error(
1298 "test_stable_sort_order",
1299 start.elapsed(),
1300 format!("Second sort failed: {}", e),
1301 )
1302 }
1303 };
1304
1305 let sorted_keys2 = match ctx.provider.download_column::<u32>(&sorted2, 0) {
1306 Ok(d) => d,
1307 Err(e) => {
1308 return TestResult::error(
1309 "test_stable_sort_order",
1310 start.elapsed(),
1311 format!("Failed to download second sorted keys: {}", e),
1312 )
1313 }
1314 };
1315
1316 let sorted_vals2 = match ctx.provider.download_column::<u32>(&sorted2, 1) {
1317 Ok(d) => d,
1318 Err(e) => {
1319 return TestResult::error(
1320 "test_stable_sort_order",
1321 start.elapsed(),
1322 format!("Failed to download second sorted vals: {}", e),
1323 )
1324 }
1325 };
1326
1327 if sorted_keys != sorted_keys2 {
1329 return TestResult::error(
1330 "test_stable_sort_order",
1331 start.elapsed(),
1332 "Two sorts produced different key orderings".to_string(),
1333 );
1334 }
1335
1336 if sorted_vals != sorted_vals2 {
1337 return TestResult::error(
1338 "test_stable_sort_order",
1339 start.elapsed(),
1340 "Two sorts produced different val orderings (sort may not be deterministic)"
1341 .to_string(),
1342 );
1343 }
1344
1345 if let Err(e) = ctx.sync_and_check() {
1346 return TestResult::error(
1347 "test_stable_sort_order",
1348 start.elapsed(),
1349 format!("Sync failed: {}", e),
1350 );
1351 }
1352
1353 TestResult::passed("test_stable_sort_order", start.elapsed())
1354}