tawasuyu/hifas
Motor en Rust para decidir, con demostración y no a ojo, cuándo un conjunto de réplicas local-first puede operar sin coordinación y cuándo no — y, cuando la respuesta es "sí, dentro de límites", proveer el mecanismo que lo garantiza.
T3: backend GF(2) exacto + migración a aritmética sin epsilon
1//! Criterio de éxito del hito M1 (§8): A da 0, B da ≠0, y la localización de B
2//! apunta al ciclo correcto (entre las réplicas 1 y 2).
3
4use sheafsync::cohomology::{compute, compute_with};
5use sheafsync::gf2::Gf2Backend;
6use sheafsync::linalg::F64Backend;
7use sheafsync::nerve::ReplicaId;
8use sheafsync::sheaf::Sheaf;
9use sheafsync::verdict::Verdict;
10
11#[test]
12fn corrida_a_corre_libre() {
13 // G-Counter compartido en camino acíclico (stalk = vector ℤ³).
14 let sheaf = Sheaf::constant(3, &[(0, 1), (1, 2)], 3);
15 let coho = compute(&sheaf).unwrap();
16 assert_eq!(coho.dim_h1, 0, "el caso monótono no tiene obstrucción");
17 assert_eq!(Verdict::diagnose(&sheaf, &coho), Verdict::RunsFree);
18}
19
20#[test]
21fn corrida_b_coordina_con_nudo_localizado() {
22 // ResettableRegister: enlace doble irreconciliable entre réplicas 1 y 2.
23 let sheaf = Sheaf::constant(3, &[(0, 1), (1, 2), (1, 2)], 1);
24 let coho = compute(&sheaf).unwrap();
25 assert_eq!(coho.dim_h1, 1, "el ciclo produce una obstrucción");
26
27 let verdict = Verdict::diagnose(&sheaf, &coho);
28 let knots = match verdict {
29 Verdict::NeedsCoordination { knots } => knots,
30 Verdict::RunsFree => panic!("debería necesitar coordinación"),
31 };
32 assert_eq!(knots.len(), 1, "un único nudo");
33 let knot = &knots[0];
34
35 // El nudo está entre las réplicas 1 y 2, NO involucra a la réplica 0.
36 assert_eq!(knot.replicas, vec![ReplicaId(1), ReplicaId(2)]);
37 // Y son las dos aristas del enlace doble (índices 1 y 2), no la 0.
38 assert_eq!(knot.edges, vec![1, 2]);
39}
40
41/// El veredicto de M1 no depende de la aritmética: `f64` (con epsilon) y `GF(2)`
42/// (exacta) coinciden en A y en B. Migración a exacto de T3.
43#[test]
44fn m1_coincide_en_f64_y_gf2() {
45 let sheaf_a = Sheaf::constant(3, &[(0, 1), (1, 2)], 3);
46 let sheaf_b = Sheaf::constant(3, &[(0, 1), (1, 2), (1, 2)], 1);
47
48 for (sheaf, esperado_h1) in [(&sheaf_a, 0), (&sheaf_b, 1)] {
49 let a = compute_with(sheaf, &F64Backend::new()).unwrap();
50 let b = compute_with(sheaf, &Gf2Backend).unwrap();
51 assert_eq!(a.dim_h1, esperado_h1);
52 assert_eq!(a.dim_h1, b.dim_h1);
53 assert_eq!(a.dim_h0, b.dim_h0);
54 // La localización coincide en ambos backends.
55 assert_eq!(Verdict::diagnose(sheaf, &a), Verdict::diagnose(sheaf, &b));
56 }
57}