gioser

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.

⎇ main tests/m1.rs Rust · 57 lines · 2.3 KB History Who touched what raw

T3: backend GF(2) exacto + migración a aritmética sin epsilon 018b9715e · Sergio ·

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}