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.

Rust 24Markdown 15TOML 1358 Definiciones

⎇ 67c1eb776f64dd43ab986286053d01f4fa8e7780 src/main.rs Rust · 119 líneas · 4.4 KB Historia Quién tocó qué crudo

T7: veredicto por difusión de Tarski y cierre de discrepancias 67c1eb776 · Sergio ·

1//! Binario de `sheafsync`. Hito M1 (§8): dos corridas de signo opuesto que
2//! prueban el núcleo entero de punta a punta.
3//!
4//! - Corrida A (G-Counter, monótono): compartición acíclica → `H¹ = 0` → corre libre.
5//! - Corrida B (ResettableRegister, no monótono): el reset introduce un enlace
6//!   irreconciliable sobre el dato ya compartido por las réplicas 1 y 2 → ciclo
7//!   localizado → `H¹ ≠ 0` → coordina ahí.
8
9use sheafsync::cell::{Cell, GCounter, ResettableRegister};
10use sheafsync::cohomology::{self, Cohomology};
11use sheafsync::oracle::{Config, Share};
12use sheafsync::sheaf::Sheaf;
13use sheafsync::verdict::Verdict;
14
15fn main() {
16    println!("sheafsync — hito M1\n");
17    corrida_a();
18    println!();
19    corrida_b();
20    println!();
21    contraste_tarski();
22}
23
24/// Imprime el veredicto de un haz ya construido.
25fn veredicto(nombre: &str, sheaf: &Sheaf) -> Cohomology {
26    let coho = cohomology::compute(sheaf).expect("haz bien formado");
27    let verdict = Verdict::diagnose(sheaf, &coho);
28    println!(
29        "  cohomología: dim H⁰ = {}, dim H¹ = {}  (rank δ = {}, dim C⁰ = {}, dim C¹ = {})",
30        coho.dim_h0, coho.dim_h1, coho.rank_delta, coho.dim_c0, coho.dim_c1
31    );
32    println!("  veredicto [{nombre}]: {verdict}");
33    coho
34}
35
36/// Corrida A — G-Counter monótono sobre 3 réplicas que comparten en camino.
37///
38/// Cada réplica incrementa solo su casilla y difunde el vector a sus vecinas.
39/// Al fundir por máximo siempre convergen; la compartición es acíclica, así que
40/// no hay obstrucción posible: `H¹ = 0`.
41fn corrida_a() {
42    println!("Corrida A — G-Counter (monótono: {})", GCounter::is_monotone());
43
44    // Demostración del CRDT: tres réplicas incrementan lo suyo y se funden.
45    let mut r0 = GCounter::zeros(3);
46    let mut r1 = GCounter::zeros(3);
47    let mut r2 = GCounter::zeros(3);
48    r0.increment(0, 5);
49    r1.increment(1, 3);
50    r2.increment(2, 7);
51    let fundido = r0.join(&r1).join(&r2);
52    println!(
53        "  merge de las 3 réplicas: {:?}  (valor total = {})",
54        fundido.slots,
55        fundido.value()
56    );
57
58    // Haz: stalk = el vector contador (ℤ³), restricciones = identidad (comparten
59    // el vector completo), topología = camino 0—1—2 (acíclica).
60    let sheaf = Sheaf::constant(3, &[(0, 1), (1, 2)], 3);
61    veredicto("A", &sheaf);
62}
63
64/// Corrida B — ResettableRegister no monótono sobre 3 réplicas en ciclo.
65///
66/// Las réplicas 1 y 2 comparten un registro. El reset (no monótono) impone un
67/// segundo enlace sobre ese mismo dato que no puede reconciliarse globalmente:
68/// forma un ciclo entre 1 y 2. El haz lo detecta y lo localiza ahí.
69fn corrida_b() {
70    println!(
71        "Corrida B — ResettableRegister (monótono: {})",
72        ResettableRegister::is_monotone()
73    );
74
75    // Demostración del CRDT: un reset con valor menor gana por generación.
76    let alto = ResettableRegister::new(10);
77    let mut reseteado = alto.clone();
78    reseteado.reset(0);
79    let fundido = alto.join(&reseteado);
80    println!(
81        "  merge tras reset: value = {} (gen {}) — el reset retracta el 10 previo",
82        fundido.value, fundido.generation
83    );
84
85    // Haz: stalk = valor del registro (escalar), restricciones = identidad.
86    // Aristas: 0—1 (single) y un DOBLE enlace 1—2 (el original + el que fuerza el
87    // reset), irreconciliables → ciclo localizado entre réplicas 1 y 2.
88    let sheaf = Sheaf::constant(3, &[(0, 1), (1, 2), (1, 2)], 1);
89    veredicto("B", &sheaf);
90}
91
92/// Contraste de la pista de retículos (Addendum §F): el triángulo monótono.
93///
94/// El motor LINEAL ve la topología del ciclo e inventa una obstrucción; la
95/// difusión de TARSKI reconcilia el dato monótono sin colapsar → corre libre,
96/// igualando a CALM. Es la brecha de modelado (§2.3) cerrada, en vivo.
97fn contraste_tarski() {
98    println!("Contraste — triángulo monótono (0—1—2—0, dato que se funde solo)");
99
100    let triangulo = Config::new(
101        "triangulo-monotono",
102        3,
103        vec![
104            Share::monotona(0, 1),
105            Share::monotona(1, 2),
106            Share::monotona(2, 0),
107        ],
108    );
109
110    println!("  oráculo (CALM):     {:?}", triangulo.oracle_verdict());
111    println!(
112        "  haz lineal (H¹):    {:?}   ← falsa obstrucción topológica",
113        triangulo.sheaf_verdict()
114    );
115    println!(
116        "  difusión de Tarski: {:?}      ← reconcilia al join, discrepancia RESUELTA",
117        triangulo.tarski_verdict()
118    );
119}