experiments/
experiments.rs1use constraint_crdt::*;
3
4fn main() {
5 println!("╔══════════════════════════════════════════════════╗");
6 println!("║ CONSTRAINT-CRDT NOVEL EXPERIMENTS ║");
7 println!("╚══════════════════════════════════════════════════╝\n");
8
9 experiment_1_bloom();
10 experiment_2_geometric();
11 experiment_3_decay();
12 experiment_4_sketch();
13}
14
15fn experiment_1_bloom() {
16 println!("=== Experiment 1: Bloom Filter CRDT ===\n");
17
18 for &n in &[1_000, 10_000, 100_000] {
19 let mut bf = bloom::BloomCRDT::new(n, 0.01);
20 let items: Vec<String> = (0..n).map(|i| format!("constraint_{}", i)).collect();
21 for item in &items { bf.insert(item); }
22
23 let mut fp = 0;
25 let trials = 100_000;
26 for i in n..n+trials {
27 if bf.contains(&format!("constraint_{}", i)) { fp += 1; }
28 }
29 let measured_fpr = fp as f64 / trials as f64;
30
31 let bloom_bytes = bf.wire_size();
33 let exact_bytes = n * 32; let compression = exact_bytes as f64 / bloom_bytes as f64;
35
36 println!(" n={:>6}: FPR={:.4} (target 0.01), space={:>6} bytes ({:.1}x compression), fill={:.2}%",
37 n, measured_fpr, bloom_bytes, compression, bf.fill_ratio() * 100.0);
38 }
39
40 let mut a = bloom::BloomCRDT::new(1000, 0.01);
42 let mut b = bloom::BloomCRDT::new(1000, 0.01);
43 for i in 0..500 { a.insert(&format!("a_{}", i)); }
44 for i in 500..1000 { b.insert(&format!("b_{}", i)); }
45 let merged = a.merged(&b);
46 let mut all_found = true;
47 for i in 0..500 { if !merged.contains(&format!("a_{}", i)) { all_found = false; } }
48 for i in 500..1000 { if !merged.contains(&format!("b_{}", i)) { all_found = false; } }
49 println!("\n Merge test: all items found after merge = {}", all_found);
50 println!();
51}
52
53fn experiment_2_geometric() {
54 println!("=== Experiment 2: Eisenstein-Geometric Gossip ===\n");
55
56 for &n in &[4, 8, 16, 32] {
57 let max_rounds = match n {
58 4 => 20, 8 => 40, 16 => 80, 32 => 150, _ => 200,
59 };
60 let result = geometric::run_experiment(n, 42, max_rounds);
61
62 let rr = result.random_convergence_rounds.unwrap_or(0);
63 let gr = result.geometric_convergence_rounds.unwrap_or(0);
64 let speedup = if gr > 0 { rr as f64 / gr as f64 } else { 0.0 };
65
66 println!(" n={:>2}: random={:>3} rounds ({:>5} msgs), geometric={:>3} rounds ({:>5} msgs), speedup={:.2}x",
67 n, rr, result.random_messages, gr, result.geometric_messages, speedup);
68 }
69 println!();
70}
71
72fn experiment_3_decay() {
73 println!("=== Experiment 3: Time-Decay Constraint CRDT ===\n");
74
75 let ns = 1_000_000_000u64;
76
77 let half_life = 300.0; let mut state = decay::DecayConstraintState::new("forgemaster", half_life);
80
81 for t in 0..60 {
83 let time = t as u64 * 60 * ns;
84 state.record_satisfied(100.0, time);
85 state.record_violations(2.0, time);
86 }
87
88 println!(" After 60 min steady state:");
89 println!(" Satisfaction rate at t=60: {:.1}%", state.satisfaction_rate(60 * 60 * ns) * 100.0);
90
91 let mut state2 = decay::DecayConstraintState::new("oracle1", half_life);
93 for t in 0..55 {
94 let time = t as u64 * 60 * ns;
95 state2.record_satisfied(100.0, time);
96 state2.record_violations(2.0, time);
97 }
98 state2.record_violations(50.0, 55 * 60 * ns);
100 state2.record_satisfied(100.0, 55 * 60 * ns);
101 for t in 56..60 {
102 let time = t as u64 * 60 * ns;
103 state2.record_satisfied(100.0, time);
104 state2.record_violations(2.0, time);
105 }
106
107 println!("\n After burst (50 violations at min 55):");
108 println!(" Satisfaction rate at t=60: {:.1}%", state2.satisfaction_rate(60 * 60 * ns) * 100.0);
109 println!(" Violation weight at t=60: {:.1}", state2.violation_weight(60 * 60 * ns));
110
111 println!("\n Half-life sensitivity:");
113 for &hl in &[30.0, 60.0, 300.0, 3600.0] {
114 let mut s = decay::DecayConstraintState::new("test", hl);
115 for t in 0..60 {
116 s.record_violations(5.0, t as u64 * 60 * ns);
117 }
118 let weight = s.violation_weight(60 * 60 * ns);
119 println!(" half_life={:>4.0}s: violation_weight={:.1}", hl, weight);
120 }
121 println!();
122}
123
124fn experiment_4_sketch() {
125 println!("=== Experiment 4: Count-Min Sketch CRDT ===\n");
126
127 let mut sketch = sketch::SketchCRDT::new(0.001, 0.01);
129
130 let n = 1_000_000;
132 let mut exact: std::collections::HashMap<String, u64> = std::collections::HashMap::new();
133
134 for i in 0..n {
135 let constraint = if i % 3 == 0 { "bounds_check" }
137 else if i % 5 == 0 { "norm_check" }
138 else if i % 7 == 0 { "holonomy" }
139 else if i % 100 == 0 { "rare_violation" }
140 else { "ok" };
141
142 if constraint != "ok" {
143 sketch.record(constraint, 1);
144 *exact.entry(constraint.to_string()).or_insert(0) += 1;
145 }
146 }
147
148 println!(" Heavy-hitter detection (1M checks):");
149 println!(" {:>20} {:>10} {:>10} {:>10}", "Constraint", "Exact", "Estimated", "Error%");
150
151 let mut items: Vec<_> = exact.iter().collect();
152 items.sort_by_key(|(_, &v)| std::cmp::Reverse(v));
153
154 for (name, &true_count) in &items {
155 let est = sketch.estimate(name);
156 let error = if true_count > 0 { (est as f64 - true_count as f64) / true_count as f64 * 100.0 } else { 0.0 };
157 println!(" {:>20} {:>10} {:>10} {:>9.1}%", name, true_count, est, error);
158 }
159
160 println!("\n Space: sketch = {} bytes, exact hash map ≈ {} bytes",
161 sketch.space_bytes(),
162 items.len() * 40); let mut a = sketch::SketchCRDT::new(0.001, 0.01);
166 let mut b = sketch::SketchCRDT::new(0.001, 0.01);
167 for _ in 0..1000 { a.record("bounds_check", 1); }
168 for _ in 0..500 { b.record("norm_check", 1); }
169 let merged = a.merged(&b);
170 println!("\n After merge: bounds_check ≥ {}, norm_check ≥ {}",
171 merged.estimate("bounds_check"), merged.estimate("norm_check"));
172 println!();
173}