use super::*;
use std::sync::Arc;
#[test]
fn test_sinkhorn_cpu_ref_trivial() {
let (u, v, _iters) = cpu_ref(
&[65536],
&[65536],
&[65536],
&[65536],
&[65536],
&[65536],
1,
1,
10,
);
assert_eq!(u, vec![65536]);
assert_eq!(v, vec![65536]);
}
#[test]
fn test_sinkhorn_cpu_ref_edge() {
let (u, _, _) = cpu_ref(
&[32768],
&[32768],
&[65536],
&[65536],
&[65536],
&[65536],
1,
1,
10,
);
assert_eq!(u, vec![0]);
}
#[test]
fn test_sinkhorn_cpu_ref_normal() {
let k = vec![65536, 65536, 65536, 65536];
let k_t = vec![65536, 65536, 65536, 65536];
let a = vec![32768, 32768];
let b = vec![32768, 32768];
let u_c = vec![65536, 65536];
let v_in = vec![65536, 65536];
let (u, _v, _) = cpu_ref(&k, &k_t, &a, &b, &u_c, &v_in, 2, 2, 5);
assert_eq!(u, vec![32768, 32768]);
}
#[test]
fn test_sinkhorn_cpu_ref_large() {
let k = vec![65536; 9];
let a = vec![65536; 3];
let b = vec![65536; 3];
let u_c = vec![65536; 3];
let v_in = vec![65536; 3];
let (u, _, _) = cpu_ref(&k, &k, &a, &b, &u_c, &v_in, 3, 3, 5);
assert_eq!(u.len(), 3);
}
#[test]
fn test_sinkhorn_cpu_ref_asym() {
let k = vec![65536, 0, 0, 65536, 65536, 65536];
let k_t = vec![65536, 0, 65536, 0, 65536, 65536];
let a = vec![32768, 32768, 65536];
let b = vec![65536, 65536];
let u_c = vec![65536, 65536, 65536];
let v_in = vec![65536, 65536];
let (u, v, _) = cpu_ref(&k, &k_t, &a, &b, &u_c, &v_in, 3, 2, 5);
assert_eq!(u.len(), 3);
assert_eq!(v.len(), 2);
}
#[test]
fn test_sinkhorn_cpu_ref_into_reuses_buffers() {
let k = vec![65536, 65536, 65536, 65536];
let a = vec![32768, 32768];
let b = vec![32768, 32768];
let u_c = vec![65536, 65536];
let v_in = vec![65536, 65536];
let mut u = Vec::with_capacity(8);
let mut v = Vec::with_capacity(8);
let mut u_old = Vec::with_capacity(8);
let u_ptr = u.as_ptr();
let v_ptr = v.as_ptr();
let old_ptr = u_old.as_ptr();
let _iters = cpu_ref_into(
&k, &k, &a, &b, &u_c, &v_in, 2, 2, 5, &mut u, &mut v, &mut u_old,
);
assert_eq!(u, vec![32768, 32768]);
assert_eq!(u.as_ptr(), u_ptr);
assert_eq!(v.as_ptr(), v_ptr);
assert_eq!(u_old.as_ptr(), old_ptr);
}
#[test]
fn test_sinkhorn_cpu_ref_into_truncates_stale_buffers() {
let k = vec![65536, 65536, 65536, 65536];
let a = vec![32768, 32768];
let b = vec![32768, 32768];
let u_c = vec![65536, 65536];
let v_in = vec![65536, 65536];
let mut u = Vec::with_capacity(8);
let mut v = Vec::with_capacity(8);
let mut u_old = Vec::with_capacity(8);
u.extend([99u32; 8]);
v.extend([99u32; 8]);
u_old.extend([99u32; 8]);
let u_ptr = u.as_ptr();
let v_ptr = v.as_ptr();
let old_ptr = u_old.as_ptr();
let _iters = try_cpu_ref_into(
&k, &k, &a, &b, &u_c, &v_in, 2, 2, 5, &mut u, &mut v, &mut u_old,
)
.unwrap();
assert_eq!(u, vec![32768, 32768]);
assert_eq!(u.as_ptr(), u_ptr);
assert_eq!(v.as_ptr(), v_ptr);
assert_eq!(u_old.as_ptr(), old_ptr);
}
#[test]
fn test_sinkhorn_try_cpu_ref_rejects_short_buffers() {
let err = try_cpu_ref(&[1], &[1], &[1, 1], &[1, 1], &[1, 1], &[1, 1], 2, 2, 1).unwrap_err();
assert!(err.contains("buffer `k` is too short"), "{err}");
}
#[test]
fn test_sinkhorn_program_parity() {
let k = vec![1, 1, 1, 1];
let a = vec![10, 10];
let b = vec![10, 10];
let u_c = vec![1, 1];
let v_in = vec![1, 1];
let p = sinkhorn_iterate(
"k", "kt", "a", "b", "uc", "un", "v", "kv", "ktu", "c", 2, 2, 1,
);
let (expected_u, _, _) = cpu_ref(&k, &k, &a, &b, &u_c, &v_in, 2, 2, 1);
use vyre_reference::reference_eval;
use vyre_reference::value::Value;
let to_value = |data: &[u32]| {
let bytes = crate::wire::pack_u32_slice(data);
Value::Bytes(Arc::from(bytes))
};
let inputs = vec![
to_value(&u_c),
to_value(&[0_u32, 0]),
to_value(&[0]),
to_value(&k),
to_value(&k),
to_value(&a),
to_value(&b),
to_value(&v_in),
to_value(&[0_u32, 0]),
to_value(&[0_u32, 0]),
];
let results = reference_eval(&p, &inputs).expect("Fix: interpreter failed");
let actual_bytes = results[0].to_bytes();
let actual_u: Vec<u32> = actual_bytes
.chunks_exact(4)
.map(|c| u32::from_le_bytes(c.try_into().unwrap()))
.collect();
assert_eq!(actual_u, expected_u);
}
#[test]
fn program_declares_ten_buffers() {
let p = sinkhorn_iterate(
"k", "kt", "a", "b", "uc", "un", "v", "kv", "ktu", "c", 2, 2, 5,
);
assert_eq!(p.buffers().len(), 10);
}
fn required_workgroups(program: &Program) -> u32 {
let elements = program
.buffers()
.iter()
.map(|buffer| buffer.count())
.max()
.unwrap_or(1);
elements.div_ceil(program.workgroup_size()[0])
}
fn changed_words(program: &Program) -> u32 {
program
.buffers()
.iter()
.find(|buffer| buffer.name() == "c")
.expect("Fix: sinkhorn_iterate must declare its convergence-flag buffer.")
.count()
}
#[test]
fn multi_workgroup_sinkhorn_never_shares_one_cleared_convergence_word() {
let program = sinkhorn_iterate(
"k", "kt", "a", "b", "uc", "un", "v", "kv", "ktu", "c", 257, 1, 8,
);
assert_eq!(
required_workgroups(&program),
2,
"Fix: a 257-element scaling vector over a 256-wide workgroup must need two workgroups."
);
assert_eq!(
changed_words(&program),
8,
"Fix: a multi-workgroup sinkhorn dispatch must use the per-iteration convergence-word protocol, not one shared cleared word."
);
}
fn count_grid_sync(nodes: &[Node]) -> usize {
nodes
.iter()
.map(|node| match node {
Node::Barrier {
ordering: vyre_foundation::MemoryOrdering::GridSync,
} => 1,
Node::If {
then, otherwise, ..
} => count_grid_sync(then) + count_grid_sync(otherwise),
Node::Loop { body, .. } | Node::Block(body) => count_grid_sync(body),
Node::Region { body, .. } => count_grid_sync(body),
_ => 0,
})
.sum()
}
#[test]
fn routing_threshold_is_the_declared_workgroup_width() {
let width = PERSISTENT_FIXPOINT_WORKGROUP_SIZE[0];
let at_width = sinkhorn_iterate(
"k", "kt", "a", "b", "uc", "un", "v", "kv", "ktu", "c", width, 1, 8,
);
assert_eq!(
at_width.workgroup_size(),
PERSISTENT_FIXPOINT_WORKGROUP_SIZE
);
assert_eq!(required_workgroups(&at_width), 1);
assert_eq!(
changed_words(&at_width),
1,
"Fix: a single-workgroup launch must keep the compact one-word convergence flag."
);
let past_width = sinkhorn_iterate(
"k",
"kt",
"a",
"b",
"uc",
"un",
"v",
"kv",
"ktu",
"c",
width + 1,
1,
8,
);
assert_eq!(required_workgroups(&past_width), 2);
assert_eq!(
changed_words(&past_width),
8,
"Fix: one cell past the workgroup width already needs the per-iteration convergence words."
);
}
#[test]
fn modest_square_matrix_with_tiny_extents_still_routes_to_the_grid_form() {
let program = sinkhorn_iterate(
"k", "kt", "a", "b", "uc", "un", "v", "kv", "ktu", "c", 17, 17, 8,
);
assert_eq!(
matrix_cells(&program),
289,
"Fix: a 17 by 17 kernel is 289 cells, past a 256-wide workgroup."
);
assert_eq!(
required_workgroups(&program),
2,
"Fix: 289 kernel cells over a 256-wide workgroup must need two workgroups."
);
assert_eq!(
changed_words(&program),
8,
"Fix: the routing threshold must be the dispatch span (max declared buffer), not m."
);
assert!(
count_grid_sync(program.entry()) > 0,
"Fix: a two-workgroup dispatch must be grid-synchronized whichever buffer widened it."
);
}
fn matrix_cells(program: &Program) -> u32 {
program
.buffers()
.iter()
.find(|buffer| buffer.name() == "k")
.expect("Fix: sinkhorn_iterate must declare its kernel matrix.")
.count()
}
#[test]
fn grid_route_fences_the_grid_and_single_workgroup_route_does_not() {
let width = PERSISTENT_FIXPOINT_WORKGROUP_SIZE[0];
let single = sinkhorn_iterate(
"k", "kt", "a", "b", "uc", "un", "v", "kv", "ktu", "c", width, 1, 4,
);
assert_eq!(
count_grid_sync(single.entry()),
0,
"Fix: a single-workgroup sinkhorn program must not force a cooperative grid launch."
);
let grid = sinkhorn_iterate(
"k", "kt", "a", "b", "uc", "un", "v", "kv", "ktu", "c", 17, 17, 4,
);
assert_eq!(
count_grid_sync(grid.entry()),
8,
"Fix: the grid form must fence each of its 4 waves twice, once after the transfer step and once after the compare."
);
}
#[test]
fn grid_route_sizes_changed_to_one_word_per_iteration() {
for max_iterations in [1_u32, 2, 8, 64] {
let program = sinkhorn_iterate(
"k",
"kt",
"a",
"b",
"uc",
"un",
"v",
"kv",
"ktu",
"c",
17,
17,
max_iterations,
);
let harness = persistent_fixpoint_grid(Vec::new(), "uc", "un", "c", 17, max_iterations);
assert_eq!(
changed_words(&program),
max_iterations,
"Fix: the grid route needs one convergence word per iteration; {max_iterations} iterations need {max_iterations} words."
);
assert_eq!(
changed_words(&program),
changed_words(&harness),
"Fix: this wrapper's `changed` declaration must match persistent_fixpoint_grid's own."
);
}
}
fn evolving_sinkhorn_fixture(m: u32, n: u32) -> (Vec<u32>, Vec<u32>, Vec<u32>, Vec<u32>, Vec<u32>) {
let cells = (m as usize) * (n as usize);
(
vec![1_u32; cells],
vec![4 * n; m as usize],
vec![12 * m; n as usize],
vec![1_u32; m as usize],
vec![1_u32; n as usize],
)
}
#[allow(clippy::too_many_arguments)]
fn run_sinkhorn(
program: &Program,
reversed: bool,
k: &[u32],
a: &[u32],
b: &[u32],
u_curr: &[u32],
v: &[u32],
changed_words: u32,
) -> (Vec<u32>, Vec<u32>) {
use vyre_reference::value::Value;
let to_value = |data: &[u32]| Value::Bytes(Arc::from(crate::wire::pack_u32_slice(data)));
let inputs = vec![
to_value(u_curr),
to_value(&vec![0_u32; u_curr.len()]),
to_value(&vec![0_u32; changed_words as usize]),
to_value(k),
to_value(k),
to_value(a),
to_value(b),
to_value(v),
to_value(&vec![0_u32; a.len()]),
to_value(&vec![0_u32; b.len()]),
];
let results = if reversed {
vyre_reference::reference_eval_lane_reversed(program, &inputs)
} else {
vyre_reference::reference_eval(program, &inputs)
}
.expect("Fix: the reference interpreter must execute the sinkhorn program.");
let decode = |value: &vyre_reference::value::Value| -> Vec<u32> {
value
.to_bytes()
.chunks_exact(4)
.map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap()))
.collect()
};
(decode(&results[0]), decode(&results[2]))
}
#[test]
fn single_word_harness_returns_a_wrong_scaling_vector_above_one_workgroup() {
let m = 257_u32;
let n = 1_u32;
let max_iterations = 4_u32;
let (k, a, b, u_curr, v) = evolving_sinkhorn_fixture(m, n);
let (expected_u, _, _) = cpu_ref(&k, &k, &a, &b, &u_curr, &v, m, n, max_iterations);
let unsound = sinkhorn_single_word_harness(
"k",
"kt",
"a",
"b",
"uc",
"un",
"v",
"kv",
"ktu",
"c",
m,
n,
max_iterations,
);
let (forward, _) = run_sinkhorn(&unsound, false, &k, &a, &b, &u_curr, &v, 1);
let (reversed, reversed_flag) = run_sinkhorn(&unsound, true, &k, &a, &b, &u_curr, &v, 1);
assert_eq!(
expected_u[256], 0,
"Fix: the CPU oracle must settle element 256 at 0 for this fixture."
);
assert_eq!(
forward[256], 0,
"Fix: stepping group 0 first must expose the SAME program as correct, proving the divergence is cross-workgroup ordering."
);
assert_eq!(
reversed[256],
4,
"Fix: this test records the OBSERVED wrong value (4, the first-sweep value) the racing shared flag produces; if the single-word harness stops diverging here, re-derive the defect before deleting this test."
);
assert_ne!(
reversed[256], expected_u[256],
"Fix: the workgroup order the IR permits must be observed disagreeing with the CPU oracle at element 256."
);
assert_eq!(
reversed_flag[0], 0,
"Fix: the shared flag must be observed claiming convergence while element 256 is wrong, which is what makes the wrong answer silent."
);
}
#[test]
fn grid_routed_sinkhorn_is_order_independent_where_single_word_diverges() {
let m = 257_u32;
let n = 1_u32;
let max_iterations = 4_u32;
let (k, a, b, u_curr, v) = evolving_sinkhorn_fixture(m, n);
let (expected_u, _, _) = cpu_ref(&k, &k, &a, &b, &u_curr, &v, m, n, max_iterations);
let routed = sinkhorn_iterate(
"k",
"kt",
"a",
"b",
"uc",
"un",
"v",
"kv",
"ktu",
"c",
m,
n,
max_iterations,
);
assert_eq!(
changed_words(&routed),
max_iterations,
"Fix: this size must route to the grid harness."
);
for reversed in [false, true] {
let (actual, _) = run_sinkhorn(&routed, reversed, &k, &a, &b, &u_curr, &v, max_iterations);
assert_eq!(
actual, expected_u,
"Fix: the grid-routed sinkhorn program must match the CPU oracle in both workgroup orders (reversed={reversed})."
);
}
}
#[test]
fn modest_square_matrix_is_correct_on_the_single_word_harness_despite_two_workgroups() {
let m = 17_u32;
let n = 17_u32;
let max_iterations = 4_u32;
let (k, a, b, u_curr, v) = evolving_sinkhorn_fixture(m, n);
let (expected_u, _, _) = cpu_ref(&k, &k, &a, &b, &u_curr, &v, m, n, max_iterations);
let unsound = sinkhorn_single_word_harness(
"k",
"kt",
"a",
"b",
"uc",
"un",
"v",
"kv",
"ktu",
"c",
m,
n,
max_iterations,
);
assert_eq!(
required_workgroups(&unsound),
2,
"Fix: 289 kernel cells must still span two workgroups, so the unsound combination is present."
);
for reversed in [false, true] {
let (actual, _) = run_sinkhorn(&unsound, reversed, &k, &a, &b, &u_curr, &v, 1);
assert_eq!(
actual, expected_u,
"Fix: with every lane gate at `t < 17` the defect is masked and 17x17 must agree with the oracle (reversed={reversed})."
);
}
}