qualia_core_db/solvers/
rope.rs1pub fn rope_interleaved(
23 vec: &mut [f64],
24 n_heads: usize,
25 head_dim: usize,
26 pos: f64,
27 base: f64,
28 scale: f64,
29) {
30 let half = head_dim / 2;
31 if half == 0 {
32 return;
33 }
34 let scale = if scale > 0.0 && scale.is_finite() {
35 scale
36 } else {
37 1.0
38 };
39 let scaled_pos = pos / scale;
40 for head in 0..n_heads {
41 let off = head * head_dim;
42 if off + head_dim > vec.len() {
43 return;
44 }
45 for i in 0..half {
46 let theta = scaled_pos * base.powf(-2.0 * i as f64 / head_dim as f64);
47 let (s, c) = theta.sin_cos();
48 let x0 = vec[off + 2 * i];
49 let x1 = vec[off + 2 * i + 1];
50 vec[off + 2 * i] = x0 * c - x1 * s;
51 vec[off + 2 * i + 1] = x0 * s + x1 * c;
52 }
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn position_zero_is_identity() {
62 let orig = [1.0, 2.0, 3.0, 4.0];
64 let mut v = orig;
65 rope_interleaved(&mut v, 1, 4, 0.0, 10000.0, 1.0);
66 for i in 0..4 {
67 assert!((v[i] - orig[i]).abs() < 1e-12);
68 }
69 }
70
71 #[test]
72 fn rotation_preserves_pair_norm() {
73 let orig = [0.3, -0.7, 1.1, 2.0, -1.5, 0.4];
75 let mut v = orig;
76 rope_interleaved(&mut v, 1, 6, 5.0, 10000.0, 1.0);
77 for i in 0..3 {
78 let n0 = orig[2 * i] * orig[2 * i] + orig[2 * i + 1] * orig[2 * i + 1];
79 let n1 = v[2 * i] * v[2 * i] + v[2 * i + 1] * v[2 * i + 1];
80 assert!(
81 (n0 - n1).abs() < 1e-9,
82 "pair {i} norm changed: {n0} -> {n1}"
83 );
84 }
85 }
86
87 #[test]
88 fn quarter_turn_known_rotation() {
89 let mut v = [2.0, 5.0];
91 rope_interleaved(&mut v, 1, 2, std::f64::consts::FRAC_PI_2, 10000.0, 1.0);
92 assert!((v[0] - (-5.0)).abs() < 1e-9, "x0 = {}", v[0]);
93 assert!((v[1] - 2.0).abs() < 1e-9, "x1 = {}", v[1]);
94 }
95
96 #[test]
97 fn per_head_blocks_independent() {
98 let mut v = [1.0, 0.0, 0.0, 1.0];
100 rope_interleaved(&mut v, 2, 2, std::f64::consts::FRAC_PI_2, 10000.0, 1.0);
101 assert!((v[0] - 0.0).abs() < 1e-9 && (v[1] - 1.0).abs() < 1e-9);
103 assert!((v[2] - (-1.0)).abs() < 1e-9 && (v[3] - 0.0).abs() < 1e-9);
104 }
105}